public function getLintErrorForNode()

in src/Linters/NamespacePrivateLinter.hack [18:130]


  public function getLintErrorForNode(
    this::TContext $context,
    this::TNode $node,
  ): ?ASTLintError {
    // Process all namespaces, use statements present in the file.
    $namespaces_details = $node->getNamespaces();

    // Go through namespace details and its uses to check for potential violations of private namespace boundaries.
    foreach ($namespaces_details as $namespace_detail_shape) {
      $current_namespace = $namespace_detail_shape['name'] ?? '';
      $namespace_uses = Shapes::toArray($namespace_detail_shape['uses']);

      foreach ($namespace_uses as $namespace_use_dict) {
        foreach ($namespace_use_dict as $aliased_ns) {
          if (
            !$this->isPrivateNamespacePathAllowed(
              $aliased_ns['name'],
              $current_namespace,
            )
          ) {
            return new ASTLintError(
              $this,
              'Artifacts belonging to a namespace are private to the parent namespace',
              $aliased_ns['use_clause'],
            );
          }
        }
      }
    }

    $traversed_qualified_names = varray[];
    foreach (
      $node->getDescendantsByType<QualifiedName>() as $qualified_name_token
    ) {
      $name_token_key = '';
      if ($qualified_name_token->hasParts()) {
        $name_token_key = $qualified_name_token->getDescendantsByType<
          NameToken,
        >()
          |> Vec\map(
            $$,
            $qualified_name_token ==> $qualified_name_token->getText(),
          )
          |> Str\join($$, '\\');
        if (qualified_name_is_fully_qualified($qualified_name_token)) {
          $name_token_key = '\\'.$name_token_key;
        }
      }

      // If a qualified token has already been traversed or has no parts, then skip it.
      if (
        Str\is_empty($name_token_key) ||
        C\contains($traversed_qualified_names, $name_token_key)
      ) {
        continue;
      }
      // Add the current qualified name token to the traversed list.
      $traversed_qualified_names[] = $name_token_key;

      // get the namespace for each qualified name token that we encounter.
      foreach ($namespaces_details as $ns) {
        if ($ns['children']->isAncestorOf($qualified_name_token)) {
          $current_namespace = $ns['name'] ?? '';
          $fully_qualified_name_for_current_token = '';

          $parent_node = $context->getParentOfDescendant($qualified_name_token);
          if (
            $parent_node is ScopeResolutionExpression ||
            $parent_node is NameExpression
          ) {
            $fully_qualified_name_for_current_token = $this->resolveScope(
              $name_token_key,
              $ns['uses']['namespaces'],
              $current_namespace,
            );
          } else if ($parent_node is FunctionCallExpression) {
            $fully_qualified_name_for_current_token = resolve_function(
              $name_token_key,
              $context,
              $qualified_name_token,
            )['name'];
          } else if ($parent_node is SimpleTypeSpecifier) {
            $fully_qualified_name_for_current_token = resolve_type(
              $name_token_key,
              $context,
              $qualified_name_token,
            )['name'];
          }
          /**
           * In some cases, the current namespace is also a qualified name, so it gets filtered into a qualified name token.
           * We do not want to check for private keyword in that case.
           */
          if ($fully_qualified_name_for_current_token === $current_namespace) {
            continue;
          }

          if (
            !$this->isPrivateNamespacePathAllowed(
              $fully_qualified_name_for_current_token,
              $current_namespace,
            )
          ) {
            return new ASTLintError(
              $this,
              'Artifacts belonging to a namespace are private to the parent namespace',
              $qualified_name_token,
            );
          }
        }
      }
    }
    return null;
  }