Repository: apple/swift
File: lib/ClangImporter/ClangImporter.cpp (C++)

Code before:
    returnTy = func->getReturnType().getTypePtrOrNull();
  else if (const auto *method = llvm::dyn_cast<clang::ObjCMethodDecl>(decl))
    returnTy = method->getReturnType().getTypePtrOrNull();

  if (!returnTy)
    return std::nullopt;

  const clang::Type *desugaredReturnTy =
      returnTy->getUnqualifiedDesugaredType();

  if (const auto *ptrType =
          llvm::dyn_cast<clang::PointerType>(desugaredReturnTy)) {
    if (const clang::RecordDecl *record =
            ptrType->getPointeeType()->getAsRecordDecl()) {
      return matchSwiftAttrConsideringInheritance<RC>(
          record, {{"returned_as_unretained_by_default", RC::Unowned}});
    }
  }

  return std::nullopt;
}

static const std::vector<std::vector<std::string>> ConflictingSwiftAttrs = {
    {"Escapable", "~Escapable", "escapable_if:"},
    {"~Copyable", "copyable_if:"},
    {"mutating", "nonmutating"}};

static inline bool isConditionalAttr(StringRef attrName) {
  return attrName == "escapable_if:" || attrName == "copyable_if:";
}

void swift::importer::validateSwiftAttributes(
    const clang::NamedDecl *decl, ClangImporter::Implementation &impl) {
  auto transformToString = [](std::vector<std::string> &attrs) {
    if (attrs.size() == 1)
      return "'" + attrs[0] + "'";
    std::string result;
    llvm::raw_string_ostream os(result);
    unsigned numAttrs = attrs.size();
    for (unsigned i = 0; i < numAttrs; ++i) {
      os << "'" << attrs[i] << "'";
      if (i < numAttrs - 2)
        os << ", ";
      else if (i == numAttrs - 2)
        os << " or ";
    }
    return result;
  };

  if (!decl->hasAttrs())
    return;

Review comment (suggestion):
Nice! There is one more group we could add: `returns_retained` and `returns_unretained`.

Code after:

  const clang::Type *returnTy = nullptr;
  if (const auto *func = llvm::dyn_cast<clang::FunctionDecl>(decl))
    returnTy = func->getReturnType().getTypePtrOrNull();
  else if (const auto *method = llvm::dyn_cast<clang::ObjCMethodDecl>(decl))
    returnTy = method->getReturnType().getTypePtrOrNull();

  if (!returnTy)
    return std::nullopt;

  const clang::Type *desugaredReturnTy =
      returnTy->getUnqualifiedDesugaredType();

  if (const auto *ptrType =
          llvm::dyn_cast<clang::PointerType>(desugaredReturnTy)) {
    if (const clang::RecordDecl *record =
            ptrType->getPointeeType()->getAsRecordDecl()) {
      return matchSwiftAttrConsideringInheritance<RC>(
          record, {{"returned_as_unretained_by_default", RC::Unowned}});
    }
  }

  return std::nullopt;
}

static const std::vector<std::vector<std::pair<StringRef, StringRef>>>
    ConflictingSwiftAttrs = {
        {{"Escapable", "SWIFT_ESCAPABLE"},
         {"~Escapable", "SWIFT_NONESCAPABLE"},
         {"escapable_if:", "SWIFT_ESCAPABLE_IF"}},
        {{"~Copyable", "SWIFT_NONCOPYABLE"},
         {"copyable_if:", "SWIFT_COPYABLE_IF"}},
        {{"mutating", "SWIFT_MUTATING"}, {"nonmutating", "'nonmutating'"}}};

static bool isConditionalAttr(StringRef attrName) {
  return attrName == "escapable_if:" || attrName == "copyable_if:";
}

void ClangImporter::Implementation::validateSwiftAttributes(
    const clang::NamedDecl *decl) {
  if (!decl->hasAttrs())
    return;

  for (const auto &groupOfAttrs : ConflictingSwiftAttrs) {
    // stores this decl's attributes, grouped by annotation kind
    llvm::StringMap<std::vector<clang::SwiftAttrAttr *>> found;
    unsigned count = 0;
    for (auto [attrName, annotationName] : groupOfAttrs) {
      for (auto attr : decl->getAttrs()) {
        auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
        if (swiftAttr && swiftAttr->getAttribute().starts_with(attrName)) {