/**
   * @param projectArtifact the main artifact of the project we're verifying (this is considered the
   *     entry point for reachability)
   * @param artifactsToCheck all artifacts that are on the runtime classpath
   * @param allArtifacts all artifacts, including implicit artifacts (runtime provided artifacts)
   * @return a list of conflicts
   */
  public ImmutableList<Conflict> check(
      Artifact projectArtifact, List<Artifact> artifactsToCheck, List<Artifact> allArtifacts) {

    final CheckerStateBuilder stateBuilder = new CheckerStateBuilder();

    createCanonicalClassMapping(stateBuilder, allArtifacts);
    CheckerState state = stateBuilder.build();

    // brute-force reachability analysis
    Set<TypeDescriptor> reachableClasses =
        reachableFrom(projectArtifact.classes().values(), state.knownClasses());

    final ImmutableList.Builder<Conflict> builder = ImmutableList.builder();

    // Then go through everything in the classpath to make sure all the method calls / field
    // references
    // are satisfied.
    for (Artifact artifact : artifactsToCheck) {
      for (DeclaredClass clazz : artifact.classes().values()) {
        if (!reachableClasses.contains(clazz.className())) {
          continue;
        }

        for (DeclaredMethod method : clazz.methods().values()) {
          checkForBrokenMethodCalls(state, artifact, clazz, method, builder);
          checkForBrokenFieldAccess(state, artifact, clazz, method, builder);
        }
      }
    }
    return builder.build();
  }