/// Emits a Swift-style dependencies file. static bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, DependencyTracker &depTracker, const FrontendOptions &opts) { if (!SF) { diags.diagnose(SourceLoc(), diag::emit_reference_dependencies_without_primary_file); return true; } std::error_code EC; llvm::raw_fd_ostream out(opts.ReferenceDependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.ReferenceDependenciesFilePath, EC.message()); out.clear_error(); return true; } auto escape = [](Identifier name) -> std::string { return llvm::yaml::escape(name.str()); }; out << "### Swift dependencies file v0 ###\n"; llvm::MapVector<const NominalTypeDecl *, bool> extendedNominals; llvm::SmallVector<const ExtensionDecl *, 8> extensionsWithJustMembers; out << "provides-top-level:\n"; for (const Decl *D : SF->Decls) { switch (D->getKind()) { case DeclKind::Module: break; case DeclKind::Import: // FIXME: Handle re-exported decls. break; case DeclKind::Extension: { auto *ED = cast<ExtensionDecl>(D); auto *NTD = ED->getExtendedType()->getAnyNominal(); if (!NTD) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() == Accessibility::Private) { break; } bool justMembers = std::all_of(ED->getInherited().begin(), ED->getInherited().end(), extendedTypeIsPrivate); if (justMembers) { if (std::all_of(ED->getMembers().begin(), ED->getMembers().end(), declIsPrivate)) { break; } else { extensionsWithJustMembers.push_back(ED); } } extendedNominals[NTD] |= !justMembers; findNominals(extendedNominals, ED->getMembers()); break; } case DeclKind::InfixOperator: case DeclKind::PrefixOperator: case DeclKind::PostfixOperator: out << "- \"" << escape(cast<OperatorDecl>(D)->getName()) << "\"\n"; break; case DeclKind::Enum: case DeclKind::Struct: case DeclKind::Class: case DeclKind::Protocol: { auto *NTD = cast<NominalTypeDecl>(D); if (!NTD->hasName()) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() == Accessibility::Private) { break; } out << "- \"" << escape(NTD->getName()) << "\"\n"; extendedNominals[NTD] |= true; findNominals(extendedNominals, NTD->getMembers()); break; } case DeclKind::TypeAlias: case DeclKind::Var: case DeclKind::Func: { auto *VD = cast<ValueDecl>(D); if (!VD->hasName()) break; if (VD->hasAccessibility() && VD->getFormalAccess() == Accessibility::Private) { break; } out << "- \"" << escape(VD->getName()) << "\"\n"; break; } case DeclKind::PatternBinding: case DeclKind::TopLevelCode: case DeclKind::IfConfig: // No action necessary. break; case DeclKind::EnumCase: case DeclKind::GenericTypeParam: case DeclKind::AssociatedType: case DeclKind::Param: case DeclKind::Subscript: case DeclKind::Constructor: case DeclKind::Destructor: case DeclKind::EnumElement: llvm_unreachable("cannot appear at the top level of a file"); } } out << "provides-nominal:\n"; for (auto entry : extendedNominals) { if (!entry.second) continue; out << "- \""; mangleTypeAsContext(out, entry.first); out << "\"\n"; } out << "provides-member:\n"; for (auto entry : extendedNominals) { out << "- [\""; mangleTypeAsContext(out, entry.first); out << "\", \"\"]\n"; } // This is also part of "provides-member". for (auto *ED : extensionsWithJustMembers) { SmallString<32> mangledName; mangleTypeAsContext(llvm::raw_svector_ostream(mangledName), ED->getExtendedType()->getAnyNominal()); for (auto *member : ED->getMembers()) { auto *VD = dyn_cast<ValueDecl>(member); if (!VD || !VD->hasName() || VD->getFormalAccess() == Accessibility::Private) { continue; } out << "- [\"" << mangledName.str() << "\", \"" << escape(VD->getName()) << "\"]\n"; } } if (SF->getASTContext().LangOpts.EnableObjCInterop) { // FIXME: This requires a traversal of the whole file to compute. // We should (a) see if there's a cheaper way to keep it up to date, // and/or (b) see if we can fast-path cases where there's no ObjC involved. out << "provides-dynamic-lookup:\n"; class ValueDeclPrinter : public VisibleDeclConsumer { private: raw_ostream &out; std::string (*escape)(Identifier); public: ValueDeclPrinter(raw_ostream &out, decltype(escape) escape) : out(out), escape(escape) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { out << "- \"" << escape(VD->getName()) << "\"\n"; } }; ValueDeclPrinter printer(out, escape); SF->lookupClassMembers({}, printer); } ReferencedNameTracker *tracker = SF->getReferencedNameTracker(); // FIXME: Sort these? out << "depends-top-level:\n"; for (auto &entry : tracker->getTopLevelNames()) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-member:\n"; auto &memberLookupTable = tracker->getUsedMembers(); using TableEntryTy = std::pair<ReferencedNameTracker::MemberPair, bool>; std::vector<TableEntryTy> sortedMembers{ memberLookupTable.begin(), memberLookupTable.end() }; llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(), [](const TableEntryTy *lhs, const TableEntryTy *rhs) -> int { if (lhs->first.first == rhs->first.first) return lhs->first.second.compare(rhs->first.second); if (lhs->first.first->getName() != rhs->first.first->getName()) return lhs->first.first->getName().compare(rhs->first.first->getName()); // Break type name ties by mangled name. SmallString<32> lhsMangledName, rhsMangledName; mangleTypeAsContext(llvm::raw_svector_ostream(lhsMangledName), lhs->first.first); mangleTypeAsContext(llvm::raw_svector_ostream(rhsMangledName), rhs->first.first); return lhsMangledName.str().compare(rhsMangledName.str()); }); for (auto &entry : sortedMembers) { assert(entry.first.first != nullptr); if (entry.first.first->hasAccessibility() && entry.first.first->getFormalAccess() == Accessibility::Private) continue; out << "- "; if (!entry.second) out << "!private "; out << "[\""; mangleTypeAsContext(out, entry.first.first); out << "\", \""; if (!entry.first.second.empty()) out << escape(entry.first.second); out << "\"]\n"; } out << "depends-nominal:\n"; for (auto i = sortedMembers.begin(), e = sortedMembers.end(); i != e; ++i) { bool isCascading = i->second; while (i+1 != e && i[0].first.first == i[1].first.first) { ++i; isCascading |= i->second; } if (i->first.first->hasAccessibility() && i->first.first->getFormalAccess() == Accessibility::Private) continue; out << "- "; if (!isCascading) out << "!private "; out << "\""; mangleTypeAsContext(out, i->first.first); out << "\"\n"; } // FIXME: Sort these? out << "depends-dynamic-lookup:\n"; for (auto &entry : tracker->getDynamicLookupNames()) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-external:\n"; for (auto &entry : depTracker.getDependencies()) { out << "- \"" << llvm::yaml::escape(entry) << "\"\n"; } llvm::SmallString<32> interfaceHash; SF->getInterfaceHash(interfaceHash); out << "interface-hash: \"" << interfaceHash << "\"\n"; return false; }
bool DeclContext::lookupQualified(Type type, DeclName member, NLOptions options, LazyResolver *typeResolver, SmallVectorImpl<ValueDecl *> &decls) const { using namespace namelookup; assert(decls.empty() && "additive lookup not supported"); if (type->is<ErrorType>()) return false; auto checkLookupCascading = [this, options]() -> Optional<bool> { switch (static_cast<unsigned>(options & NL_KnownDependencyMask)) { case 0: return isCascadingContextForLookup(/*excludeFunctions=*/false); case NL_KnownNonCascadingDependency: return false; case NL_KnownCascadingDependency: return true; case NL_KnownNoDependency: return None; default: // FIXME: Use llvm::CountPopulation_64 when that's declared constexpr. static_assert(__builtin_popcountll(NL_KnownDependencyMask) == 2, "mask should only include four values"); llvm_unreachable("mask only includes four values"); } }; // Look through lvalue and inout types. type = type->getLValueOrInOutObjectType(); // Look through metatypes. if (auto metaTy = type->getAs<AnyMetatypeType>()) type = metaTy->getInstanceType(); // Look through DynamicSelf. if (auto dynamicSelf = type->getAs<DynamicSelfType>()) type = dynamicSelf->getSelfType(); // Look for module references. if (auto moduleTy = type->getAs<ModuleType>()) { Module *module = moduleTy->getModule(); auto topLevelScope = getModuleScopeContext(); if (module == topLevelScope->getParentModule()) { if (auto maybeLookupCascade = checkLookupCascading()) { recordLookupOfTopLevelName(topLevelScope, member, maybeLookupCascade.getValue()); } lookupInModule(module, /*accessPath=*/{}, member, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, typeResolver, topLevelScope); } else { // Note: This is a lookup into another module. Unless we're compiling // multiple modules at once, or if the other module re-exports this one, // it shouldn't be possible to have a dependency from that module on // anything in this one. // Perform the lookup in all imports of this module. forAllVisibleModules(this, [&](const Module::ImportedModule &import) -> bool { if (import.second != module) return true; lookupInModule(import.second, import.first, member, decls, NLKind::QualifiedLookup, ResolutionKind::Overloadable, typeResolver, topLevelScope); // If we're able to do an unscoped lookup, we see everything. No need // to keep going. return !import.first.empty(); }); } llvm::SmallPtrSet<ValueDecl *, 4> knownDecls; decls.erase(std::remove_if(decls.begin(), decls.end(), [&](ValueDecl *vd) -> bool { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(vd)) return true; return !knownDecls.insert(vd).second; }), decls.end()); if (auto *debugClient = topLevelScope->getParentModule()->getDebugClient()) filterForDiscriminator(decls, debugClient); return !decls.empty(); } auto &ctx = getASTContext(); if (!ctx.LangOpts.EnableAccessControl) options |= NL_IgnoreAccessibility; // The set of nominal type declarations we should (and have) visited. SmallVector<NominalTypeDecl *, 4> stack; llvm::SmallPtrSet<NominalTypeDecl *, 4> visited; // Handle nominal types. bool wantProtocolMembers = false; bool wantLookupInAllClasses = false; if (auto nominal = type->getAnyNominal()) { visited.insert(nominal); stack.push_back(nominal); wantProtocolMembers = (options & NL_ProtocolMembers) && !isa<ProtocolDecl>(nominal); // If we want dynamic lookup and we're searching in the // AnyObject protocol, note this for later. if (options & NL_DynamicLookup) { if (auto proto = dyn_cast<ProtocolDecl>(nominal)) { if (proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) wantLookupInAllClasses = true; } } } // Handle archetypes else if (auto archetypeTy = type->getAs<ArchetypeType>()) { // Look in the protocols to which the archetype conforms (always). for (auto proto : archetypeTy->getConformsTo()) if (visited.insert(proto).second) stack.push_back(proto); // If requested, look into the superclasses of this archetype. if (options & NL_VisitSupertypes) { if (auto superclassTy = archetypeTy->getSuperclass()) { if (auto superclassDecl = superclassTy->getAnyNominal()) { if (visited.insert(superclassDecl).second) { stack.push_back(superclassDecl); wantProtocolMembers = (options & NL_ProtocolMembers) && !isa<ProtocolDecl>(superclassDecl); } } } } } // Handle protocol compositions. else if (auto compositionTy = type->getAs<ProtocolCompositionType>()) { SmallVector<ProtocolDecl *, 4> protocols; if (compositionTy->isExistentialType(protocols)) { for (auto proto : protocols) { if (visited.insert(proto).second) { stack.push_back(proto); // If we want dynamic lookup and this is the AnyObject // protocol, note this for later. if ((options & NL_DynamicLookup) && proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) wantLookupInAllClasses = true; } } } } // Allow filtering of the visible declarations based on various // criteria. bool onlyCompleteObjectInits = false; auto isAcceptableDecl = [&](NominalTypeDecl *current, Decl *decl) -> bool { // If the decl is currently being type checked, then we have something // cyclic going on. Instead of poking at parts that are potentially not // set up, just assume it is acceptable. This will make sure we produce an // error later. if (decl->isBeingTypeChecked()) return true; // Filter out designated initializers, if requested. if (onlyCompleteObjectInits) { if (auto ctor = dyn_cast<ConstructorDecl>(decl)) { if (!ctor->isInheritable()) return false; } else { return false; } } // Ignore stub implementations. if (auto ctor = dyn_cast<ConstructorDecl>(decl)) { if (ctor->hasStubImplementation()) return false; } // Check access. if (!(options & NL_IgnoreAccessibility)) if (auto VD = dyn_cast<ValueDecl>(decl)) return VD->isAccessibleFrom(this); return true; }; ReferencedNameTracker *tracker = nullptr; if (auto containingSourceFile = dyn_cast<SourceFile>(getModuleScopeContext())) tracker = containingSourceFile->getReferencedNameTracker(); bool isLookupCascading; if (tracker) { if (auto maybeLookupCascade = checkLookupCascading()) isLookupCascading = maybeLookupCascade.getValue(); else tracker = nullptr; } // Visit all of the nominal types we know about, discovering any others // we need along the way. while (!stack.empty()) { auto current = stack.back(); stack.pop_back(); if (tracker) tracker->addUsedMember({current, member.getBaseName()},isLookupCascading); // Make sure we've resolved implicit constructors, if we need them. if (member.getBaseName() == ctx.Id_init && typeResolver) typeResolver->resolveImplicitConstructors(current); // Look for results within the current nominal type and its extensions. bool currentIsProtocol = isa<ProtocolDecl>(current); for (auto decl : current->lookupDirect(member)) { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(decl)) continue; // Resolve the declaration signature when we find the // declaration. if (typeResolver && !decl->isBeingTypeChecked()) { typeResolver->resolveDeclSignature(decl); if (!decl->hasType()) continue; } if (isAcceptableDecl(current, decl)) decls.push_back(decl); } // If we're not supposed to visit our supertypes, we're done. if ((options & NL_VisitSupertypes) == 0) continue; // Visit superclass. if (auto classDecl = dyn_cast<ClassDecl>(current)) { // If we're looking for initializers, only look at the superclass if the // current class permits inheritance. Even then, only find complete // object initializers. bool visitSuperclass = true; if (member.getBaseName() == ctx.Id_init) { if (classDecl->inheritsSuperclassInitializers(typeResolver)) onlyCompleteObjectInits = true; else visitSuperclass = false; } if (visitSuperclass) { if (auto superclassType = classDecl->getSuperclass()) if (auto superclassDecl = superclassType->getClassOrBoundGenericClass()) if (visited.insert(superclassDecl).second) stack.push_back(superclassDecl); } } // If we're not looking at a protocol and we're not supposed to // visit the protocols that this type conforms to, skip the next // step. if (!wantProtocolMembers && !currentIsProtocol) continue; SmallVector<ProtocolDecl *, 4> protocols; for (auto proto : current->getAllProtocols()) { if (visited.insert(proto).second) { stack.push_back(proto); } } // For a class, we don't need to visit the protocol members of the // superclass: that's already handled. if (isa<ClassDecl>(current)) wantProtocolMembers = false; } // If we want to perform lookup into all classes, do so now. if (wantLookupInAllClasses) { if (tracker) tracker->addDynamicLookupName(member.getBaseName(), isLookupCascading); // Collect all of the visible declarations. SmallVector<ValueDecl *, 4> allDecls; forAllVisibleModules(this, [&](Module::ImportedModule import) { import.second->lookupClassMember(import.first, member, allDecls); }); // For each declaration whose context is not something we've // already visited above, add it to the list of declarations. llvm::SmallPtrSet<ValueDecl *, 4> knownDecls; for (auto decl : allDecls) { // If we're performing a type lookup, don't even attempt to validate // the decl if its not a type. if ((options & NL_OnlyTypes) && !isa<TypeDecl>(decl)) continue; if (typeResolver && !decl->isBeingTypeChecked()) { typeResolver->resolveDeclSignature(decl); if (!decl->hasType()) continue; } // If the declaration has an override, name lookup will also have // found the overridden method. Skip this declaration, because we // prefer the overridden method. if (decl->getOverriddenDecl()) continue; auto dc = decl->getDeclContext(); auto nominal = dyn_cast<NominalTypeDecl>(dc); if (!nominal) { auto ext = cast<ExtensionDecl>(dc); nominal = ext->getExtendedType()->getAnyNominal(); assert(nominal && "Couldn't find nominal type?"); } // If we didn't visit this nominal type above, add this // declaration to the list. if (!visited.count(nominal) && knownDecls.insert(decl).second && isAcceptableDecl(nominal, decl)) decls.push_back(decl); } } // If we're supposed to remove overridden declarations, do so now. if (options & NL_RemoveOverridden) removeOverriddenDecls(decls); // If we're supposed to remove shadowed/hidden declarations, do so now. Module *M = getParentModule(); if (options & NL_RemoveNonVisible) removeShadowedDecls(decls, M, typeResolver); if (auto *debugClient = M->getDebugClient()) filterForDiscriminator(decls, debugClient); // We're done. Report success/failure. return !decls.empty(); }
bool swift::emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, DependencyTracker &depTracker, const FrontendOptions &opts) { if (!SF) { diags.diagnose(SourceLoc(), diag::emit_reference_dependencies_without_primary_file); return true; } // Before writing to the dependencies file path, preserve any previous file // that may have been there. No error handling -- this is just a nicety, it // doesn't matter if it fails. llvm::sys::fs::rename(opts.ReferenceDependenciesFilePath, opts.ReferenceDependenciesFilePath + "~"); std::error_code EC; llvm::raw_fd_ostream out(opts.ReferenceDependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.ReferenceDependenciesFilePath, EC.message()); out.clear_error(); return true; } auto escape = [](Identifier name) -> std::string { return llvm::yaml::escape(name.str()); }; out << "### Swift dependencies file v0 ###\n"; llvm::MapVector<const NominalTypeDecl *, bool> extendedNominals; llvm::SmallVector<const FuncDecl *, 8> memberOperatorDecls; llvm::SmallVector<const ExtensionDecl *, 8> extensionsWithJustMembers; out << "provides-top-level:\n"; for (const Decl *D : SF->Decls) { switch (D->getKind()) { case DeclKind::Module: break; case DeclKind::Import: // FIXME: Handle re-exported decls. break; case DeclKind::Extension: { auto *ED = cast<ExtensionDecl>(D); auto *NTD = ED->getExtendedType()->getAnyNominal(); if (!NTD) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() <= Accessibility::FilePrivate) { break; } // Check if the extension is just adding members, or if it is // introducing a conformance to a public protocol. bool justMembers = std::all_of(ED->getInherited().begin(), ED->getInherited().end(), extendedTypeIsPrivate); if (justMembers) { if (std::all_of(ED->getMembers().begin(), ED->getMembers().end(), declIsPrivate)) { break; } else { extensionsWithJustMembers.push_back(ED); } } extendedNominals[NTD] |= !justMembers; findNominalsAndOperators(extendedNominals, memberOperatorDecls, ED->getMembers()); break; } case DeclKind::InfixOperator: case DeclKind::PrefixOperator: case DeclKind::PostfixOperator: out << "- \"" << escape(cast<OperatorDecl>(D)->getName()) << "\"\n"; break; case DeclKind::PrecedenceGroup: out << "- \"" << escape(cast<PrecedenceGroupDecl>(D)->getName()) << "\"\n"; break; case DeclKind::Enum: case DeclKind::Struct: case DeclKind::Class: case DeclKind::Protocol: { auto *NTD = cast<NominalTypeDecl>(D); if (!NTD->hasName()) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() <= Accessibility::FilePrivate) { break; } out << "- \"" << escape(NTD->getName()) << "\"\n"; extendedNominals[NTD] |= true; findNominalsAndOperators(extendedNominals, memberOperatorDecls, NTD->getMembers()); break; } case DeclKind::TypeAlias: case DeclKind::Var: case DeclKind::Func: { auto *VD = cast<ValueDecl>(D); if (!VD->hasName()) break; if (VD->hasAccessibility() && VD->getFormalAccess() <= Accessibility::FilePrivate) { break; } out << "- \"" << escape(VD->getName()) << "\"\n"; break; } case DeclKind::PatternBinding: case DeclKind::TopLevelCode: case DeclKind::IfConfig: // No action necessary. break; case DeclKind::EnumCase: case DeclKind::GenericTypeParam: case DeclKind::AssociatedType: case DeclKind::Param: case DeclKind::Subscript: case DeclKind::Constructor: case DeclKind::Destructor: case DeclKind::EnumElement: llvm_unreachable("cannot appear at the top level of a file"); } } // This is also part of "provides-top-level". for (auto *operatorFunction : memberOperatorDecls) out << "- \"" << escape(operatorFunction->getName()) << "\"\n"; out << "provides-nominal:\n"; for (auto entry : extendedNominals) { if (!entry.second) continue; out << "- \""; out << mangleTypeAsContext(entry.first); out << "\"\n"; } out << "provides-member:\n"; for (auto entry : extendedNominals) { out << "- [\""; out << mangleTypeAsContext(entry.first); out << "\", \"\"]\n"; } // This is also part of "provides-member". for (auto *ED : extensionsWithJustMembers) { auto mangledName = mangleTypeAsContext( ED->getExtendedType()->getAnyNominal()); for (auto *member : ED->getMembers()) { auto *VD = dyn_cast<ValueDecl>(member); if (!VD || !VD->hasName() || VD->getFormalAccess() <= Accessibility::FilePrivate) { continue; } out << "- [\"" << mangledName << "\", \"" << escape(VD->getName()) << "\"]\n"; } } if (SF->getASTContext().LangOpts.EnableObjCInterop) { // FIXME: This requires a traversal of the whole file to compute. // We should (a) see if there's a cheaper way to keep it up to date, // and/or (b) see if we can fast-path cases where there's no ObjC involved. out << "provides-dynamic-lookup:\n"; class NameCollector : public VisibleDeclConsumer { private: SmallVector<Identifier, 16> names; public: void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { names.push_back(VD->getName()); } ArrayRef<Identifier> getNames() { llvm::array_pod_sort(names.begin(), names.end(), [](const Identifier *lhs, const Identifier *rhs) { return lhs->compare(*rhs); }); names.erase(std::unique(names.begin(), names.end()), names.end()); return names; } }; NameCollector collector; SF->lookupClassMembers({}, collector); for (Identifier name : collector.getNames()) { out << "- \"" << escape(name) << "\"\n"; } } ReferencedNameTracker *tracker = SF->getReferencedNameTracker(); auto sortedByIdentifier = [](const llvm::DenseMap<Identifier, bool> map) -> SmallVector<std::pair<Identifier, bool>, 16> { SmallVector<std::pair<Identifier, bool>, 16> pairs{map.begin(), map.end()}; llvm::array_pod_sort(pairs.begin(), pairs.end(), [](const std::pair<Identifier, bool> *first, const std::pair<Identifier, bool> *second) -> int { return first->first.compare(second->first); }); return pairs; }; out << "depends-top-level:\n"; for (auto &entry : sortedByIdentifier(tracker->getTopLevelNames())) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-member:\n"; auto &memberLookupTable = tracker->getUsedMembers(); using TableEntryTy = std::pair<ReferencedNameTracker::MemberPair, bool>; std::vector<TableEntryTy> sortedMembers{ memberLookupTable.begin(), memberLookupTable.end() }; llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(), [](const TableEntryTy *lhs, const TableEntryTy *rhs) -> int { if (lhs->first.first == rhs->first.first) return lhs->first.second.compare(rhs->first.second); if (lhs->first.first->getName() != rhs->first.first->getName()) return lhs->first.first->getName().compare(rhs->first.first->getName()); // Break type name ties by mangled name. auto lhsMangledName = mangleTypeAsContext(lhs->first.first); auto rhsMangledName = mangleTypeAsContext(rhs->first.first); return lhsMangledName.compare(rhsMangledName); }); for (auto &entry : sortedMembers) { assert(entry.first.first != nullptr); if (entry.first.first->hasAccessibility() && entry.first.first->getFormalAccess() <= Accessibility::FilePrivate) continue; out << "- "; if (!entry.second) out << "!private "; out << "[\""; out << mangleTypeAsContext(entry.first.first); out << "\", \""; if (!entry.first.second.empty()) out << escape(entry.first.second); out << "\"]\n"; } out << "depends-nominal:\n"; for (auto i = sortedMembers.begin(), e = sortedMembers.end(); i != e; ++i) { bool isCascading = i->second; while (i+1 != e && i[0].first.first == i[1].first.first) { ++i; isCascading |= i->second; } if (i->first.first->hasAccessibility() && i->first.first->getFormalAccess() <= Accessibility::FilePrivate) continue; out << "- "; if (!isCascading) out << "!private "; out << "\""; out << mangleTypeAsContext(i->first.first); out << "\"\n"; } out << "depends-dynamic-lookup:\n"; for (auto &entry : sortedByIdentifier(tracker->getDynamicLookupNames())) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-external:\n"; for (auto &entry : reversePathSortedFilenames(depTracker.getDependencies())) { out << "- \"" << llvm::yaml::escape(entry) << "\"\n"; } llvm::SmallString<32> interfaceHash; SF->getInterfaceHash(interfaceHash); out << "interface-hash: \"" << interfaceHash << "\"\n"; return false; }