<commit_before>#include "swift/xcode-autobuilder/XcodeProjectParser.h"
#include "swift/xcode-autobuilder/XcodeWorkspaceParser.h"
#include "swift/xcode-autobuilder/CFHelpers.h"

#include <iostream>
#include <filesystem>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <CoreFoundation/CoreFoundation.h>

namespace fs = std::filesystem;

struct TargetData {
  std::string workspace;
  std::string project;
  std::string type;
};

typedef std::unordered_map<std::string, CFDictionaryRef> Targets;
typedef std::unordered_map<std::string, std::vector<std::string>> Dependencies;
typedef std::unordered_map<std::string, std::vector<std::pair<std::string, CFDictionaryRef>>>
    BuildFiles;

static size_t totalFilesCount(const std::string& target,
                              const Dependencies& dependencies,
                              const BuildFiles& buildFiles) {
  size_t sum = buildFiles.at(target).size();
  for (auto& dep : dependencies.at(target)) {
    sum += totalFilesCount(dep, dependencies, buildFiles);
  }
  return sum;
}

static bool objectIsTarget(CFDictionaryRef object) {
  auto isa = cf_string_ref(CFDictionaryGetValue(object, CFSTR("isa")));
  if (isa) {
    for (auto target :
         {CFSTR("PBXAggregateTarget"), CFSTR("PBXNativeTarget"), CFSTR("PBXLegacyTarget")}) {
      if (CFStringCompare(isa, target, 0) == kCFCompareEqualTo) {
        return true;
      }
    }
  }
  return false;
}

static void mapTargetsToSourceFiles(CFDictionaryRef objects,
                                    std::unordered_map<std::string, size_t>& fileCounts) {
  Targets targets;
  Dependencies dependencies;
  BuildFiles buildFiles;

  auto kv = CFKeyValues::fromDictionary(objects);
  for (size_t i = 0; i < kv.size; i++) {
    auto object = cf_dictionary_ref(kv.values[i]);
    if (objectIsTarget(object)) {
      auto name = stringValueForKey(object, CFSTR("name"));
      dependencies[name] = {};
      buildFiles[name] = {};
      targets.emplace(name, object);
    }
  }

  for (auto& [targetName, targetObject] : targets) {
    auto deps = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("dependencies")));
    auto size = CFArrayGetCount(deps);
    for (CFIndex i = 0; i < size; i++) {
      auto dependencyID = cf_string_ref(CFArrayGetValueAtIndex(deps, i));
      auto dependency = cf_dictionary_ref(CFDictionaryGetValue(objects, dependencyID));
      auto targetID = cf_string_ref(CFDictionaryGetValue(dependency, CFSTR("target")));
      if (!targetID) {
        // Skipping non-targets (e.g., productRef)
        continue;
      }
      auto targetDependency = cf_dictionary_ref(CFDictionaryGetValue(objects, targetID));
      auto dependencyName = stringValueForKey(targetDependency, CFSTR("name"));
      if (!dependencyName.empty()) {
        dependencies[targetName].push_back(dependencyName);
      }
    }
  }

  for (auto& [targetName, targetObject] : targets) {
    auto buildPhases = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("buildPhases")));
    auto buildPhaseCount = CFArrayGetCount(buildPhases);
    for (CFIndex buildPhaseIndex = 0; buildPhaseIndex < buildPhaseCount; buildPhaseIndex++) {
      auto buildPhaseID = cf_string_ref(CFArrayGetValueAtIndex(buildPhases, buildPhaseIndex));
      auto buildPhase = cf_dictionary_ref(CFDictionaryGetValue(objects, buildPhaseID));
      auto fileRefs = cf_array_ref(CFDictionaryGetValue(buildPhase, CFSTR("files")));
      if (!fileRefs) {
        continue;
      }
      auto fileRefsCount = CFArrayGetCount(fileRefs);
      for (CFIndex fileRefIndex = 0; fileRefIndex < fileRefsCount; fileRefIndex++) {
        auto fileRefID = cf_string_ref(CFArrayGetValueAtIndex(fileRefs, fileRefIndex));
        auto fileRef = cf_dictionary_ref(CFDictionaryGetValue(objects, fileRefID));
        auto fileID = cf_string_ref(CFDictionaryGetValue(fileRef, CFSTR("fileRef")));
        if (!fileID) {
          // FileRef is not a reference to a file (e.g., PBXBuildFile)
          continue;
        }
        auto file = cf_dictionary_ref(CFDictionaryGetValue(objects, fileID));
        if (!file) {
          // Sometimes the references file belongs to another project, which is not present for
          // various reasons
          continue;
        }
        auto isa = stringValueForKey(file, CFSTR("isa"));
        if (isa != "PBXFileReference") {
          // Skipping anything that is not a 'file', e.g. PBXVariantGroup
          continue;
        }
        auto fileType = stringValueForKey(file, CFSTR("lastKnownFileType"));
        auto path = stringValueForKey(file, CFSTR("path"));
        if (fileType == "sourcecode.swift" && !path.empty()) {
          buildFiles[targetName].emplace_back(path, file);
        }
      }
    }
  }

  for (auto& [targetName, _] : targets) {
    fileCounts[targetName] = totalFilesCount(targetName, dependencies, buildFiles);
  }
}

static CFDictionaryRef xcodeProjectObjects(const std::string& xcodeProject) {
  auto allocator = CFAllocatorGetDefault();
  auto pbxproj = fs::path(xcodeProject) / "project.pbxproj";
  if (!fs::exists(pbxproj)) {
    return CFDictionaryCreate(allocator, nullptr, nullptr, 0, nullptr, nullptr);
  }
  std::ifstream ifs(pbxproj, std::ios::in);
  std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
  auto data = CFDataCreate(allocator, (UInt8*)content.data(), content.size());
  CFErrorRef error = nullptr;
  auto plist = CFPropertyListCreateWithData(allocator, data, 0, nullptr, &error);
  if (error) {
    std::cerr << "[xcode autobuilder] Cannot read Xcode project: ";<commit_msg>Swift: do not use C casts<commit_after>#include "swift/xcode-autobuilder/XcodeProjectParser.h"
#include "swift/xcode-autobuilder/XcodeWorkspaceParser.h"
#include "swift/xcode-autobuilder/CFHelpers.h"

#include <iostream>
#include <filesystem>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <CoreFoundation/CoreFoundation.h>

namespace fs = std::filesystem;

struct TargetData {
  std::string workspace;
  std::string project;
  std::string type;
};

typedef std::unordered_map<std::string, CFDictionaryRef> Targets;
typedef std::unordered_map<std::string, std::vector<std::string>> Dependencies;
typedef std::unordered_map<std::string, std::vector<std::pair<std::string, CFDictionaryRef>>>
    BuildFiles;

static size_t totalFilesCount(const std::string& target,
                              const Dependencies& dependencies,
                              const BuildFiles& buildFiles) {
  size_t sum = buildFiles.at(target).size();
  for (auto& dep : dependencies.at(target)) {
    sum += totalFilesCount(dep, dependencies, buildFiles);
  }
  return sum;
}

static bool objectIsTarget(CFDictionaryRef object) {
  auto isa = cf_string_ref(CFDictionaryGetValue(object, CFSTR("isa")));
  if (isa) {
    for (auto target :
         {CFSTR("PBXAggregateTarget"), CFSTR("PBXNativeTarget"), CFSTR("PBXLegacyTarget")}) {
      if (CFStringCompare(isa, target, 0) == kCFCompareEqualTo) {
        return true;
      }
    }
  }
  return false;
}

static void mapTargetsToSourceFiles(CFDictionaryRef objects,
                                    std::unordered_map<std::string, size_t>& fileCounts) {
  Targets targets;
  Dependencies dependencies;
  BuildFiles buildFiles;

  auto kv = CFKeyValues::fromDictionary(objects);
  for (size_t i = 0; i < kv.size; i++) {
    auto object = cf_dictionary_ref(kv.values[i]);
    if (objectIsTarget(object)) {
      auto name = stringValueForKey(object, CFSTR("name"));
      dependencies[name] = {};
      buildFiles[name] = {};
      targets.emplace(name, object);
    }
  }

  for (auto& [targetName, targetObject] : targets) {
    auto deps = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("dependencies")));
    auto size = CFArrayGetCount(deps);
    for (CFIndex i = 0; i < size; i++) {
      auto dependencyID = cf_string_ref(CFArrayGetValueAtIndex(deps, i));
      auto dependency = cf_dictionary_ref(CFDictionaryGetValue(objects, dependencyID));
      auto targetID = cf_string_ref(CFDictionaryGetValue(dependency, CFSTR("target")));
      if (!targetID) {
        // Skipping non-targets (e.g., productRef)
        continue;
      }
      auto targetDependency = cf_dictionary_ref(CFDictionaryGetValue(objects, targetID));
      auto dependencyName = stringValueForKey(targetDependency, CFSTR("name"));
      if (!dependencyName.empty()) {
        dependencies[targetName].push_back(dependencyName);
      }
    }
  }

  for (auto& [targetName, targetObject] : targets) {
    auto buildPhases = cf_array_ref(CFDictionaryGetValue(targetObject, CFSTR("buildPhases")));
    auto buildPhaseCount = CFArrayGetCount(buildPhases);
    for (CFIndex buildPhaseIndex = 0; buildPhaseIndex < buildPhaseCount; buildPhaseIndex++) {
      auto buildPhaseID = cf_string_ref(CFArrayGetValueAtIndex(buildPhases, buildPhaseIndex));
      auto buildPhase = cf_dictionary_ref(CFDictionaryGetValue(objects, buildPhaseID));
      auto fileRefs = cf_array_ref(CFDictionaryGetValue(buildPhase, CFSTR("files")));
      if (!fileRefs) {
        continue;
      }
      auto fileRefsCount = CFArrayGetCount(fileRefs);
      for (CFIndex fileRefIndex = 0; fileRefIndex < fileRefsCount; fileRefIndex++) {
        auto fileRefID = cf_string_ref(CFArrayGetValueAtIndex(fileRefs, fileRefIndex));
        auto fileRef = cf_dictionary_ref(CFDictionaryGetValue(objects, fileRefID));
        auto fileID = cf_string_ref(CFDictionaryGetValue(fileRef, CFSTR("fileRef")));
        if (!fileID) {
          // FileRef is not a reference to a file (e.g., PBXBuildFile)
          continue;
        }
        auto file = cf_dictionary_ref(CFDictionaryGetValue(objects, fileID));
        if (!file) {
          // Sometimes the references file belongs to another project, which is not present for
          // various reasons
          continue;
        }
        auto isa = stringValueForKey(file, CFSTR("isa"));
        if (isa != "PBXFileReference") {
          // Skipping anything that is not a 'file', e.g. PBXVariantGroup
          continue;
        }
        auto fileType = stringValueForKey(file, CFSTR("lastKnownFileType"));
        auto path = stringValueForKey(file, CFSTR("path"));
        if (fileType == "sourcecode.swift" && !path.empty()) {
          buildFiles[targetName].emplace_back(path, file);
        }
      }
    }
  }

  for (auto& [targetName, _] : targets) {
    fileCounts[targetName] = totalFilesCount(targetName, dependencies, buildFiles);
  }
}

static CFDictionaryRef xcodeProjectObjects(const std::string& xcodeProject) {
  auto allocator = CFAllocatorGetDefault();
  auto pbxproj = fs::path(xcodeProject) / "project.pbxproj";
  if (!fs::exists(pbxproj)) {
    return CFDictionaryCreate(allocator, nullptr, nullptr, 0, nullptr, nullptr);
  }
  std::ifstream ifs(pbxproj, std::ios::in);
  std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
  auto data = CFDataCreate(allocator, reinterpret_cast<UInt8*>(content.data()), content.size());
  CFErrorRef error = nullptr;
  auto plist = CFPropertyListCreateWithData(allocator, data, 0, nullptr, &error);
  if (error) {
    std::cerr << "[xcode autobuilder] Cannot read Xcode project: ";
{'fraction_non_alphanumeric': 0.12262475581601846, 'fraction_numerical': 0.0014207068016338128, 'mean_word_length': 3.8927019982623805, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 34, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1683011', 'n_tokens_mistral': 3469, 'n_tokens_neox': 3394, 'n_words': 721}