<commit_before>package com.mikesamuel.cil.ast.passes;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.annotation.Nullable;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.mikesamuel.cil.ast.AdditionalBoundNode;
import com.mikesamuel.cil.ast.AnnotationNode;
import com.mikesamuel.cil.ast.BaseNode;
import com.mikesamuel.cil.ast.BasePackageNode;
import com.mikesamuel.cil.ast.ClassOrInterfaceTypeNode;
import com.mikesamuel.cil.ast.CompilationUnitNode;
import com.mikesamuel.cil.ast.IdentifierNode;
import com.mikesamuel.cil.ast.InterfaceTypeListNode;
import com.mikesamuel.cil.ast.InterfaceTypeNode;
import com.mikesamuel.cil.ast.ModifierNode;
import com.mikesamuel.cil.ast.TypeArgumentNode;
import com.mikesamuel.cil.ast.TypeVariableNode;
import com.mikesamuel.cil.ast.meta.Name;
import com.mikesamuel.cil.ast.meta.Name.Type;
import com.mikesamuel.cil.ast.meta.TypeInfo;
import com.mikesamuel.cil.ast.meta.TypeInfoResolver;
import com.mikesamuel.cil.ast.meta.TypeNameResolver;
import com.mikesamuel.cil.ast.traits.TypeDeclaration;
import com.mikesamuel.cil.ast.traits.TypeScope;
import com.mikesamuel.cil.parser.SourcePosition;

class DeclarationPass implements AbstractPass<Void> {
  final Logger logger;

  static final boolean DEBUG = false;

  DeclarationPass(Logger logger) {
    this.logger = logger;
  }

  protected void error(@Nullable BaseNode node, String message) {
    SourcePosition pos = node != null ? node.getSourcePosition() : null;
    String fullMessage = pos != null ? pos + ": " + message : message;
    logger.severe(fullMessage);
  }

  /**
   * A resolver for types not defined in the compilation units being processed.
   */
  protected TypeInfoResolver getFallbackTypeInfoResolver() {
    ClassLoader cl = getClass().getClassLoader();
    if (cl == null) { cl = ClassLoader.getSystemClassLoader(); }
    return TypeInfoResolver.Resolvers.forClassLoader(cl);
  }

  @Override
  public Void run(
      Iterable<? extends CompilationUnitNode> compilationUnits) {
    // To properly map type names to canonical type names, we need four things:
    // 1. The set of internally defined types.
    // 2. The set of external types.
    // 3. The set of imports that map unqualified names to qualified names.
    // 4. Enough meta-data for both (1) and (2) to map non-canonical type names
    //    to canonical type names.
    // (3) requires (1, 2) so before wildcard imports can be resolved.
    // (4) requires (1, 2, 3) so we can resolve super-type relationships.

    ImmutableList<CompilationUnitNode> cus =
        ImmutableList.copyOf(compilationUnits);
    // (1)
    ClassNamingPass.DeclarationsAndScopes declarationsAndScopes =
        new ClassNamingPass(logger).run(cus);
    // (2)
    TypeInfoResolver externalTypeInfoResolver = getFallbackTypeInfoResolver();

    ScopeResolver sr = new ScopeResolver(
        declarationsAndScopes,
        externalTypeInfoResolver);
    // (3) on demand for (4)
    sr.resolveAll();

    return null;
  }

  final class ScopeResolver {
    /** TypeInfoResolver for canonical type names. */
    final TypeInfoResolver typeInfoResolver;
    /**
     * TypeInfoResolver for qualified and unqualified names in scope.
     * Built progressively as types are resolved.
     */
    final Map<TypeScope, TypeNameResolver> resolvedScopes =
        new IdentityHashMap<>();
    /**
     * Converts external type names
     */
    final TypeNameResolver canonicalizer;
    final ImmutableMap<Name, UnresolvedTypeDeclaration> internallyDefinedTypes;
    final Map<TypeScope, TypeScope> scopeToParent;
    final Multimap<TypeScope, UnresolvedTypeDeclaration> scopeToDecls =
        Multimaps.newMultimap(
            new IdentityHashMap<>(),
            new Supplier<Collection<UnresolvedTypeDeclaration>>() {
              @Override
              public Collection<UnresolvedTypeDeclaration> get() {
                return new LinkedHashSet<>();
              }
            });

    ScopeResolver(
        ClassNamingPass.DeclarationsAndScopes declarationsAndScopes,
        TypeInfoResolver externalTypeInfoResolver) {
      this.internallyDefinedTypes = declarationsAndScopes.declarations;
      this.scopeToParent = declarationsAndScopes.scopeToParent;
      this.typeInfoResolver = TypeInfoResolver.Resolvers.eitherOr(
          new TypeInfoResolver() {
            @Override
            public Optional<TypeInfo> resolve(Name typeName) {
              UnresolvedTypeDeclaration d =
                  internallyDefinedTypes.get(typeName);
              return d != null
                  ? Optional.of(d.decl.getDeclaredTypeInfo())
                  : Optional.absent();
            }
          },
          externalTypeInfoResolver);
      this.canonicalizer = TypeNameResolver.Resolvers
          .canonicalizer(typeInfoResolver);

      for (UnresolvedTypeDeclaration d : internallyDefinedTypes.values()) {
        scopeToDecls.put(d.scope, d);
      }
    }

    void resolveAll() {
      Set<Name> loop = new LinkedHashSet<>();
      for (UnresolvedTypeDeclaration d : this.internallyDefinedTypes.values()) {
        loop.clear();
        resolve(d, loop);
      }
    }

    private void resolve(UnresolvedTypeDeclaration d, Set<Name> loop) {
      switch (d.stage) {
        case UNRESOLVED:
          Preconditions.checkState(
              loop.add(d.decl.getDeclaredTypeInfo().canonName));
          d.stage = Stage.IN_PROGRESS;
          doResolveDeclaration(d, loop);
          return;
        case IN_PROGRESS:
          d.stage = Stage.UNRESOLVABLE;
          error(
              (BaseNode) d.decl,
              "Dependency loop for type declaration "
              + d.decl.getDeclaredTypeInfo().canonName
              + " : " + loop);
          return;
        case UNRESOLVABLE:  // :(
        case RESOLVED:      // :)<commit_msg>modified DeclarationPass to make available the type resolver for external and internal types<commit_after>package com.mikesamuel.cil.ast.passes;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.annotation.Nullable;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.mikesamuel.cil.ast.AdditionalBoundNode;
import com.mikesamuel.cil.ast.AnnotationNode;
import com.mikesamuel.cil.ast.BaseNode;
import com.mikesamuel.cil.ast.BasePackageNode;
import com.mikesamuel.cil.ast.ClassOrInterfaceTypeNode;
import com.mikesamuel.cil.ast.CompilationUnitNode;
import com.mikesamuel.cil.ast.IdentifierNode;
import com.mikesamuel.cil.ast.InterfaceTypeListNode;
import com.mikesamuel.cil.ast.InterfaceTypeNode;
import com.mikesamuel.cil.ast.ModifierNode;
import com.mikesamuel.cil.ast.TypeArgumentNode;
import com.mikesamuel.cil.ast.TypeVariableNode;
import com.mikesamuel.cil.ast.meta.Name;
import com.mikesamuel.cil.ast.meta.Name.Type;
import com.mikesamuel.cil.ast.meta.TypeInfo;
import com.mikesamuel.cil.ast.meta.TypeInfoResolver;
import com.mikesamuel.cil.ast.meta.TypeNameResolver;
import com.mikesamuel.cil.ast.traits.TypeDeclaration;
import com.mikesamuel.cil.ast.traits.TypeScope;
import com.mikesamuel.cil.parser.SourcePosition;

class DeclarationPass implements AbstractPass<TypeInfoResolver> {
  final Logger logger;

  static final boolean DEBUG = false;

  DeclarationPass(Logger logger) {
    this.logger = logger;
  }

  protected void error(@Nullable BaseNode node, String message) {
    SourcePosition pos = node != null ? node.getSourcePosition() : null;
    String fullMessage = pos != null ? pos + ": " + message : message;
    logger.severe(fullMessage);
  }

  /**
   * A resolver for types not defined in the compilation units being processed.
   */
  protected TypeInfoResolver getFallbackTypeInfoResolver() {
    ClassLoader cl = getClass().getClassLoader();
    if (cl == null) { cl = ClassLoader.getSystemClassLoader(); }
    return TypeInfoResolver.Resolvers.forClassLoader(cl);
  }

  /**
   * @return a TypeInfoResolver that resolves canonical names.
   */
  @Override
  public TypeInfoResolver run(
      Iterable<? extends CompilationUnitNode> compilationUnits) {
    // To properly map type names to canonical type names, we need four things:
    // 1. The set of internally defined types.
    // 2. The set of external types.
    // 3. The set of imports that map unqualified names to qualified names.
    // 4. Enough meta-data for both (1) and (2) to map non-canonical type names
    //    to canonical type names.
    // (3) requires (1, 2) so before wildcard imports can be resolved.
    // (4) requires (1, 2, 3) so we can resolve super-type relationships.

    ImmutableList<CompilationUnitNode> cus =
        ImmutableList.copyOf(compilationUnits);
    // (1)
    ClassNamingPass.DeclarationsAndScopes declarationsAndScopes =
        new ClassNamingPass(logger).run(cus);
    // (2)
    TypeInfoResolver externalTypeInfoResolver = getFallbackTypeInfoResolver();

    ScopeResolver sr = new ScopeResolver(
        declarationsAndScopes,
        externalTypeInfoResolver);
    // (3) on demand for (4)
    sr.resolveAll();

    return sr.typeInfoResolver;
  }

  final class ScopeResolver {
    /** TypeInfoResolver for canonical type names. */
    final TypeInfoResolver typeInfoResolver;
    /**
     * TypeInfoResolver for qualified and unqualified names in scope.
     * Built progressively as types are resolved.
     */
    final Map<TypeScope, TypeNameResolver> resolvedScopes =
        new IdentityHashMap<>();
    /**
     * Converts external type names
     */
    final TypeNameResolver canonicalizer;
    final ImmutableMap<Name, UnresolvedTypeDeclaration> internallyDefinedTypes;
    final Map<TypeScope, TypeScope> scopeToParent;
    final Multimap<TypeScope, UnresolvedTypeDeclaration> scopeToDecls =
        Multimaps.newMultimap(
            new IdentityHashMap<>(),
            new Supplier<Collection<UnresolvedTypeDeclaration>>() {
              @Override
              public Collection<UnresolvedTypeDeclaration> get() {
                return new LinkedHashSet<>();
              }
            });

    ScopeResolver(
        ClassNamingPass.DeclarationsAndScopes declarationsAndScopes,
        TypeInfoResolver externalTypeInfoResolver) {
      this.internallyDefinedTypes = declarationsAndScopes.declarations;
      this.scopeToParent = declarationsAndScopes.scopeToParent;
      this.typeInfoResolver = TypeInfoResolver.Resolvers.eitherOr(
          new TypeInfoResolver() {
            @Override
            public Optional<TypeInfo> resolve(Name typeName) {
              UnresolvedTypeDeclaration d =
                  internallyDefinedTypes.get(typeName);
              return d != null
                  ? Optional.of(d.decl.getDeclaredTypeInfo())
                  : Optional.absent();
            }
          },
          externalTypeInfoResolver);
      this.canonicalizer = TypeNameResolver.Resolvers
          .canonicalizer(typeInfoResolver);

      for (UnresolvedTypeDeclaration d : internallyDefinedTypes.values()) {
        scopeToDecls.put(d.scope, d);
      }
    }

    void resolveAll() {
      Set<Name> loop = new LinkedHashSet<>();
      for (UnresolvedTypeDeclaration d : this.internallyDefinedTypes.values()) {
        loop.clear();
        resolve(d, loop);
      }
      for (TypeScope scope : scopeToParent.keySet()) {
        resolverForScope(scope, new HashSet<Name>());
      }
    }

    private void resolve(UnresolvedTypeDeclaration d, Set<Name> loop) {
      switch (d.stage) {
        case UNRESOLVED:
          Preconditions.checkState(
              loop.add(d.decl.getDeclaredTypeInfo().canonName));
          d.stage = Stage.IN_PROGRESS;
          doResolveDeclaration(d, loop);
          return;
        case IN_PROGRESS:
          d.stage = Stage.UNRESOLVABLE;
          error(
              (BaseNode) d.decl,
              "Dependency loop for type declaration "
              + d.decl.getDeclaredTypeInfo().canonName
              + " : " + loop);
          return;
        case UNRESOLVABLE:  // :(
        case RESOLVED:      // :)
{'fraction_non_alphanumeric': 0.09522356172745307, 'fraction_numerical': 0.0025942316496261253, 'mean_word_length': 3.111355081555834, 'pattern_counts': {'":': 2, '<': 38, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2768822', 'n_tokens_mistral': 3766, 'n_tokens_neox': 3714, 'n_words': 828}