Files
swift-mirror/lib/FrontendTool/ReferenceDependencies.cpp
Jordan Rose 1c651973c3 Excise "Accessibility" from the compiler (2/3)
"Accessibility" has a different meaning for app developers, so we've
already deliberately excised it from our diagnostics in favor of terms
like "access control" and "access level". Do the same in the compiler
now that we aren't constantly pulling things into the release branch.

This commit changes the 'Accessibility' enum to be named 'AccessLevel'.
2017-08-28 11:34:44 -07:00

429 lines
13 KiB
C++

//===--- ReferenceDependencies.cpp - Generates swiftdeps files ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "ReferenceDependencies.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Module.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ReferencedNameTracker.h"
#include "swift/AST/Types.h"
#include "swift/Frontend/FrontendOptions.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLParser.h"
using namespace swift;
static void findNominalsAndOperators(
llvm::MapVector<const NominalTypeDecl *, bool> &foundNominals,
llvm::SmallVectorImpl<const FuncDecl *> &foundOperators,
DeclRange members) {
for (const Decl *D : members) {
auto *VD = dyn_cast<ValueDecl>(D);
if (!VD)
continue;
if (VD->hasAccess() &&
VD->getFormalAccess() <= AccessLevel::FilePrivate) {
continue;
}
if (VD->getFullName().isOperator()) {
foundOperators.push_back(cast<FuncDecl>(VD));
continue;
}
auto nominal = dyn_cast<NominalTypeDecl>(D);
if (!nominal)
continue;
foundNominals[nominal] |= true;
findNominalsAndOperators(foundNominals, foundOperators,
nominal->getMembers());
}
}
static bool declIsPrivate(const Decl *member) {
auto *VD = dyn_cast<ValueDecl>(member);
if (!VD) {
switch (member->getKind()) {
case DeclKind::Import:
case DeclKind::PatternBinding:
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
return true;
case DeclKind::Extension:
case DeclKind::InfixOperator:
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
return false;
default:
llvm_unreachable("everything else is a ValueDecl");
}
}
return VD->getFormalAccess() <= AccessLevel::FilePrivate;
}
static bool extendedTypeIsPrivate(TypeLoc inheritedType) {
auto type = inheritedType.getType();
if (!type)
return true;
if (!type->isExistentialType()) {
// Be conservative. We don't know how to deal with other extended types.
return false;
}
auto layout = type->getExistentialLayout();
assert(!layout.superclass && "Should not have a subclass existential "
"in the inheritance clause of an extension");
for (auto protoTy : layout.getProtocols()) {
if (!declIsPrivate(protoTy->getDecl()))
return false;
}
return true;
}
static std::string mangleTypeAsContext(const NominalTypeDecl *type) {
Mangle::ASTMangler Mangler;
return Mangler.mangleTypeAsContextUSR(type);
}
std::vector<std::string>
swift::reversePathSortedFilenames(const ArrayRef<std::string> elts) {
std::vector<std::string> tmp(elts.begin(), elts.end());
std::sort(tmp.begin(), tmp.end(), [](const std::string &a,
const std::string &b) -> bool {
return std::lexicographical_compare(a.rbegin(), a.rend(),
b.rbegin(), b.rend());
});
return tmp;
}
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 = [](DeclBaseName name) -> std::string {
return llvm::yaml::escape(name.userFacingName());
};
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->hasAccess() &&
NTD->getFormalAccess() <= AccessLevel::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->hasAccess() &&
NTD->getFormalAccess() <= AccessLevel::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->hasAccess() &&
VD->getFormalAccess() <= AccessLevel::FilePrivate) {
break;
}
out << "- \"" << escape(VD->getBaseName()) << "\"\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:
case DeclKind::MissingMember:
// These can occur in malformed ASTs.
break;
}
}
// 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() <= AccessLevel::FilePrivate) {
continue;
}
out << "- [\"" << mangledName << "\", \""
<< escape(VD->getBaseName()) << "\"]\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<DeclBaseName, 16> names;
public:
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override {
names.push_back(VD->getBaseName());
}
ArrayRef<DeclBaseName> getNames() {
llvm::array_pod_sort(names.begin(), names.end(),
[](const DeclBaseName *lhs,
const DeclBaseName *rhs) {
return lhs->compare(*rhs);
});
names.erase(std::unique(names.begin(), names.end()), names.end());
return names;
}
};
NameCollector collector;
SF->lookupClassMembers({}, collector);
for (DeclBaseName name : collector.getNames()) {
out << "- \"" << escape(name) << "\"\n";
}
}
ReferencedNameTracker *tracker = SF->getReferencedNameTracker();
auto sortedByName =
[](const llvm::DenseMap<DeclBaseName, bool> map) ->
SmallVector<std::pair<DeclBaseName, bool>, 16> {
SmallVector<std::pair<DeclBaseName,bool>, 16> pairs{map.begin(), map.end()};
llvm::array_pod_sort(pairs.begin(), pairs.end(),
[](const std::pair<DeclBaseName, bool> *first,
const std::pair<DeclBaseName, bool> *second) -> int{
return first->first.compare(second->first);
});
return pairs;
};
out << "depends-top-level:\n";
for (auto &entry : sortedByName(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->hasAccess() &&
entry.first.first->getFormalAccess() <= AccessLevel::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->hasAccess() &&
i->first.first->getFormalAccess() <= AccessLevel::FilePrivate)
continue;
out << "- ";
if (!isCascading)
out << "!private ";
out << "\"";
out << mangleTypeAsContext(i->first.first);
out << "\"\n";
}
out << "depends-dynamic-lookup:\n";
for (auto &entry : sortedByName(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;
}