Address review feedback

This commit is contained in:
Robert Widmann
2020-03-02 10:53:31 -08:00
parent 011c14d61c
commit f85ec3825f
10 changed files with 199 additions and 197 deletions

View File

@@ -21,6 +21,7 @@
#include "swift/Basic/LLVM.h"
namespace swift {
class DependencyTracker;
class FileUnit;
class SourceManager;
class SourceFile;
@@ -37,8 +38,10 @@ namespace swift {
bool verifyDiagnostics(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
bool autoApplyFixes, bool ignoreUnknown);
bool verifyDependencies(SourceManager &SM, ArrayRef<FileUnit *> SFs);
bool verifyDependencies(SourceManager &SM, ArrayRef<SourceFile *> SFs);
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
ArrayRef<FileUnit *> SFs);
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
ArrayRef<SourceFile *> SFs);
}
#endif

View File

@@ -264,7 +264,7 @@ public:
bool DisableInterfaceFileLock = false;
/// Should we enable the dependency verifier for all primary files known to this frontend?
bool VerifyDependencies = false;
bool EnableIncrementalDependencyVerifier = false;
/// The different modes for validating TBD against the LLVM IR.
enum class TBDValidationMode {

View File

@@ -179,7 +179,7 @@ static void validateDebugInfoArgs(DiagnosticEngine &diags,
diags.diagnose(SourceLoc(), diag::error_invalid_debug_prefix_map, A);
}
static void validateVerifyIncrementalArgs(DiagnosticEngine &diags,
static void validateVerifyIncrementalDependencyArgs(DiagnosticEngine &diags,
const ArgList &args) {
// No option? No problem!
if (!args.hasArg(options::OPT_verify_incremental_dependencies)) {
@@ -257,7 +257,7 @@ static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
validateCompilationConditionArgs(diags, args);
validateSearchPathArgs(diags, args);
validateAutolinkingArgs(diags, args, T);
validateVerifyIncrementalArgs(diags, args);
validateVerifyIncrementalDependencyArgs(diags, args);
}
std::unique_ptr<ToolChain>

View File

@@ -176,7 +176,7 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.EnableSourceImport |= Args.hasArg(OPT_enable_source_import);
Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module);
Opts.VerifyDependencies |= Args.hasArg(OPT_verify_incremental_dependencies);
Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies);
computeImportObjCHeaderOptions();
computeImplicitImportModuleNames();

View File

@@ -15,9 +15,11 @@
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/SourceFile.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Basic/OptionSet.h"
#include "swift/Frontend/DiagnosticVerifier.h"
#include "swift/Parse/Lexer.h"
@@ -40,9 +42,10 @@ namespace {
/// An expectation contains additional information about the expectation
/// \c Kind, which matches one of the few kinds of dependency entry that are
/// currently representable in the dependency graph, and an expectation
/// \c Flavor which must either be \c private or \c cascading.
/// \c Scope which must either be \c private or \c cascading.
///
/// The supported set of expectations is given by the following matrix:
/// As not all combinations of scopes and kinds makes sense, and to ease the addition of further
/// combinations of the two, the supported set of expectations is given by the following matrix:
///
#define EXPECTATION_MATRIX \
MATRIX_ENTRY("expected-no-dependency", None, Negative) \
@@ -55,6 +58,15 @@ namespace {
MATRIX_ENTRY("expected-cascading-member", Cascading, Member) \
MATRIX_ENTRY("expected-private-dynamic-member", Private, DynamicMember) \
MATRIX_ENTRY("expected-cascading-dynamic-member", Cascading, DynamicMember)
///
/// To add a new supported combination, update \c Expectation::Kind and
/// \c Expectation::Scope, then define a new \c MATRIX_ENTRY with the following information:
///
/// MATRIX_ENTRY(<Expectation-Selector-String>, Expectation::Scope, Expectation::Kind)
///
/// Where \c <Expectation-Selector-String> matches the grammar for an expectation. The
/// verifier's parsing routines use this matrix to automatically keep the parser in harmony with the
/// internal representation of the expectation.
struct Expectation {
public:
enum class Kind : uint8_t {
@@ -67,9 +79,14 @@ public:
DynamicMember,
};
enum class Flavor : uint8_t {
enum class Scope : uint8_t {
/// There is no scope information associated with this expectation.
///
/// This is currently only true of negative expectations and provides expectations.
None,
/// The dependency does not cascade.
Private,
/// The dependency cascades.
Cascading,
};
@@ -77,38 +94,46 @@ public:
const char *ExpectedStart, *ExpectedEnd = nullptr;
/// Additional information about the expectation.
std::pair<Expectation::Kind, Expectation::Flavor> Info;
struct {
Expectation::Kind Kind;
Expectation::Scope Scope;
} Info;
/// The raw input buffer for the message text, the part in the {{...}}
StringRef MessageRange;
public:
Expectation(const char *ExpectedStart, Expectation::Kind k,
Expectation::Flavor f)
: ExpectedStart(ExpectedStart), Info{k, f} {}
Expectation(const char *estart, const char *eend, Expectation::Kind k,
Expectation::Scope f, StringRef r)
: ExpectedStart(estart), ExpectedEnd(eend), Info{k, f}, MessageRange(r) {
assert(ExpectedStart <= MessageRange.data() &&
"Message range appears before expected start!");
assert(MessageRange.data()+MessageRange.size() <= ExpectedEnd &&
"Message range extends beyond expected end!");
}
bool isCascading() const {
return Info.second == Expectation::Flavor::Cascading;
return Info.Scope == Expectation::Scope::Cascading;
}
};
/// An \c Obligation represents a compiler-provided entry in the set of
/// dependencies for a given source file. Similar to an \c Expectation an \c
/// Obligation contains a name, information about its kind and flavor, and an
/// dependencies for a given source file. Similar to an \c Expectation an
/// \c Obligation contains a name, information about its kind and flavor, and an
/// extra source location that can be used to guide where diagnostics are
/// emitted. Unlike an \c Obligation, it provides an extra piece of state that
/// emitted. Unlike an \c Expectation, it provides an extra piece of state that
/// represents the obligation's "fulfillment status".
///
/// All \c Obligations begin in the \c Active state. Once an obligation has been
/// All \c Obligations begin in the \c Owed state. Once an obligation has been
/// paired with a matching \c Expectation, the obligation may then transition to
/// either \c Fulfilled if it has been satisfied completely, or \c Failed
/// otherwise. The verifier turns all unfulfilled obligations into errors.
struct Obligation {
/// The state of an \c Obligation
enum class State : uint8_t {
/// The \c Obligation is active and has not been paired with a corresponding
/// The \c Obligation is owed and has not been paired with a corresponding
/// \c Expectation.
Active,
Owed,
/// The \c Obligation is fulfilled.
Fulfilled,
/// The \c Obligation was matched against an \c Expectation, but that
@@ -119,6 +144,9 @@ struct Obligation {
/// A token returned when an \c Obligation is fulfilled or failed. An \c
/// Obligation is the only type that may construct fulfillment tokens.
///
/// \c FullfillmentToken prevents misuse of the \c Obligation
/// structure by requiring its state to be changed along all program paths.
struct FullfillmentToken {
friend Obligation;
@@ -160,18 +188,7 @@ struct Obligation {
}
static Key forExpectation(const Expectation &E) {
switch (E.Info.first) {
case Expectation::Kind::Negative:
return Key::forNegative(E.MessageRange);
case Expectation::Kind::Provides:
return Key::forProvides(E.MessageRange);
case Expectation::Kind::Member:
return Key::forMember(E.MessageRange);
case Expectation::Kind::PotentialMember:
return Key::forPotentialMember(E.MessageRange);
case Expectation::Kind::DynamicMember:
return Key::forDynamicMember(E.MessageRange);
}
return Key{E.MessageRange, E.Info.Kind};
}
public:
@@ -195,48 +212,43 @@ struct Obligation {
};
private:
DeclBaseName name;
std::pair<Expectation::Kind, Expectation::Flavor> info;
SourceLoc contextLoc;
StringRef name;
std::pair<Expectation::Kind, Expectation::Scope> info;
State state;
public:
Obligation(DeclBaseName name, Expectation::Kind k, Expectation::Flavor f,
SourceLoc loc)
: name(name), info{k, f}, contextLoc{loc}, state(State::Active) {
Obligation(StringRef name, Expectation::Kind k, Expectation::Scope f)
: name(name), info{k, f}, state(State::Owed) {
assert(k != Expectation::Kind::Negative &&
"Cannot form negative obligation!");
}
Expectation::Kind getKind() const { return info.first; }
DeclBaseName getName() const { return name; }
StringRef getName() const { return name; }
bool getCascades() const {
return info.second == Expectation::Flavor::Cascading;
return info.second == Expectation::Scope::Cascading;
}
StringRef describeCascade() const {
switch (info.second) {
case Expectation::Flavor::None:
case Expectation::Scope::None:
llvm_unreachable("Cannot describe obligation with no cascade info");
case Expectation::Flavor::Private:
case Expectation::Scope::Private:
return "non-cascading";
case Expectation::Flavor::Cascading:
case Expectation::Scope::Cascading:
return "cascading";
}
}
public:
SourceLoc getLocation() const { return contextLoc; }
public:
bool isActive() const { return state == State::Active; }
bool isOwed() const { return state == State::Owed; }
FullfillmentToken fullfill() {
assert(state == State::Active &&
assert(state == State::Owed &&
"Cannot fulfill an obligation more than once!");
state = State::Fulfilled;
return FullfillmentToken{};
}
FullfillmentToken fail() {
assert(state == State::Active &&
assert(state == State::Owed &&
"Cannot fail an obligation more than once!");
state = State::Failed;
return FullfillmentToken{};
@@ -248,11 +260,12 @@ public:
/// in the referenced name trackers associated with that file.
class DependencyVerifier {
SourceManager &SM;
const DependencyTracker &DT;
std::vector<llvm::SMDiagnostic> Errors = {};
llvm::BumpPtrAllocator StringAllocator;
public:
explicit DependencyVerifier(SourceManager &SM) : SM(SM) {}
explicit DependencyVerifier(SourceManager &SM, const DependencyTracker &DT)
: SM(SM), DT(DT) {}
bool verifyFile(const SourceFile *SF);
@@ -263,6 +276,7 @@ public:
using NegativeExpectationMap = llvm::StringMap<Expectation>;
private:
/// These routines return \c true on failure, \c false otherwise.
bool parseExpectations(const SourceFile *SF,
std::vector<Expectation> &Expectations);
bool constructObligations(const SourceFile *SF, ObligationMap &map);
@@ -279,7 +293,7 @@ private:
private:
/// Given an \c ObligationMap and an \c Expectation, attempt to identify a
/// corresponding active \c Obligation and verify it. If there is a matching
/// corresponding owed \c Obligation and verify it. If there is a matching
/// obligation, the \p fulfill callback is given the obligation. Otherwise \p
/// fail is called with the unmatched expectation value.
void matchExpectationOrFail(
@@ -289,32 +303,28 @@ private:
auto entry = OM.find(Obligation::Key::forExpectation(expectation));
if (entry == OM.end()) {
return fail(expectation);
}
} else {
fulfill(entry->second);
}
}
/// For each active \c Obligation, call the provided callback with its
/// For each owed \c Obligation, call the provided callback with its
/// relevant name data and the Obligation itself.
void
forEachActiveObligation(ObligationMap &OM,
forEachOwedObligation(ObligationMap &OM,
llvm::function_ref<void(StringRef, Obligation &)> f) {
for (auto &p : OM) {
if (p.second.isActive())
if (p.second.isOwed())
f(p.first.Name, p.second);
}
}
private:
StringRef bumpAllocateData(const char *Data, size_t Length) {
char *Ptr = StringAllocator.Allocate<char>(Length + 1);
memcpy(Ptr, Data, Length);
*(Ptr + Length) = 0;
return StringRef(Ptr, Length);
}
StringRef allocateString(StringRef S) {
return bumpAllocateData(S.data(), S.size());
StringRef copyDemangledTypeName(ASTContext &Ctx, StringRef str) {
Demangle::DemangleOptions Opts;
Opts.ShowPrivateDiscriminators = false;
Opts.DisplayModuleNames = true;
return Ctx.AllocateCopy(Demangle::demangleTypeAsString(str, Opts));
}
private:
@@ -353,8 +363,8 @@ bool DependencyVerifier::parseExpectations(
}
const auto BufferID = MaybeBufferID.getValue();
CharSourceRange EntireRange = SM.getRangeForBuffer(BufferID);
StringRef InputFile = SM.extractText(EntireRange);
const CharSourceRange EntireRange = SM.getRangeForBuffer(BufferID);
const StringRef InputFile = SM.extractText(EntireRange);
for (size_t Match = InputFile.find("expected-"); Match != StringRef::npos;
Match = InputFile.find("expected-", Match + 1)) {
@@ -362,13 +372,13 @@ bool DependencyVerifier::parseExpectations(
const char *DiagnosticLoc = MatchStart.data();
Expectation::Kind ExpectedKind;
Expectation::Flavor ExpectedFlavor;
Expectation::Scope ExpectedScope;
{
#define MATRIX_ENTRY(STRING, FLAVOR, KIND) \
.StartsWith(STRING, [&]() { \
#define MATRIX_ENTRY(EXPECTATION_SELECTOR, SCOPE, KIND) \
.StartsWith(EXPECTATION_SELECTOR, [&]() { \
ExpectedKind = Expectation::Kind::KIND; \
ExpectedFlavor = Expectation::Flavor::FLAVOR; \
MatchStart = MatchStart.substr(strlen(STRING)); \
ExpectedScope = Expectation::Scope::SCOPE; \
MatchStart = MatchStart.substr(strlen(EXPECTATION_SELECTOR)); \
})
// clang-format off
@@ -382,34 +392,32 @@ bool DependencyVerifier::parseExpectations(
// Skip any whitespace before the {{.
MatchStart = MatchStart.substr(MatchStart.find_first_not_of(" \t"));
size_t TextStartIdx = MatchStart.find("{{");
const size_t TextStartIdx = MatchStart.find("{{");
if (TextStartIdx == StringRef::npos) {
addError(MatchStart.data(), "expected {{ in expectation");
continue;
}
Expectation Expected(DiagnosticLoc, ExpectedKind, ExpectedFlavor);
size_t End = MatchStart.find("}}");
const size_t End = MatchStart.find("}}");
if (End == StringRef::npos) {
addError(MatchStart.data(),
"didn't find '}}' to match '{{' in expectation");
continue;
}
llvm::SmallString<256> Buf;
Expected.MessageRange = MatchStart.slice(2, End);
// Check if the next expectation should be in the same line.
StringRef AfterEnd = MatchStart.substr(End + strlen("}}"));
AfterEnd = AfterEnd.substr(AfterEnd.find_first_not_of(" \t"));
Expected.ExpectedEnd = AfterEnd.data();
const char *ExpectedEnd = AfterEnd.data();
// Strip out the trailing whitespace.
while (isspace(Expected.ExpectedEnd[-1]))
--Expected.ExpectedEnd;
while (isspace(ExpectedEnd[-1]))
--ExpectedEnd;
Expectations.push_back(Expected);
Expectations.emplace_back(DiagnosticLoc, ExpectedEnd,
ExpectedKind, ExpectedScope,
MatchStart.slice(2, End));
}
return false;
}
@@ -419,57 +427,53 @@ bool DependencyVerifier::constructObligations(const SourceFile *SF,
auto *tracker = SF->getReferencedNameTracker();
assert(tracker && "Constructed source file without referenced name tracker!");
// Top-level names match via unqualified references.
for (const auto &p : tracker->getTopLevelNames()) {
auto str = p.getFirst().userFacingName();
Obligations.insert({Obligation::Key::forProvides(str),
{p.getFirst(), Expectation::Kind::Provides,
Expectation::Flavor::None, SourceLoc()}});
auto &Ctx = SF->getASTContext();
tracker->enumerateAllUses(
/*includeIntrafileDeps*/ true, DT,
[&](const fine_grained_dependencies::NodeKind kind, StringRef context,
StringRef name, const bool isCascadingUse) {
using NodeKind = fine_grained_dependencies::NodeKind;
switch (kind) {
case NodeKind::externalDepend:
// We only care about the referenced name trackers for now. The set of
// external dependencies is often quite a large subset of the SDK.
return;
case NodeKind::nominal:
// Nominals duplicate member entries. We care about the member itself.
return;
case NodeKind::potentialMember: {
auto key = copyDemangledTypeName(Ctx, context);
Obligations.insert({Obligation::Key::forPotentialMember(key),
{name, Expectation::Kind::PotentialMember,
isCascadingUse ? Expectation::Scope::Cascading
: Expectation::Scope::Private}});
}
// Dynamic member names match via unqualified references.
for (const auto &p : tracker->getDynamicLookupNames()) {
auto str = p.getFirst().userFacingName();
Obligations.insert({Obligation::Key::forDynamicMember(str),
{p.getFirst(), Expectation::Kind::DynamicMember,
p.getSecond() ? Expectation::Flavor::Cascading
: Expectation::Flavor::Private,
SourceLoc()}});
}
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
PrintOptions Options = PrintOptions::printEverything();
Options.FullyQualifiedTypes = true;
for (const auto &p : tracker->getUsedMembers()) {
Str.clear();
StreamPrinter printer{OS};
auto *nominalDeclContext = p.getFirst().first;
nominalDeclContext->getDeclaredType().print(printer, Options);
// Potential members match via qualified reference to the subject.
auto memberName = p.getFirst().second;
if (memberName.empty()) {
auto key = allocateString(OS.str());
Obligations.insert(
{Obligation::Key::forPotentialMember(key),
{p.getFirst().second, Expectation::Kind::PotentialMember,
p.getSecond() ? Expectation::Flavor::Cascading
: Expectation::Flavor::Private,
SourceLoc()}});
} else {
// Member constraints match via fully-qualified name.
OS << "." << memberName.userFacingName();
auto key = allocateString(OS.str());
break;
case NodeKind::member: {
auto demContext = copyDemangledTypeName(Ctx, context);
auto key = Ctx.AllocateCopy((demContext + "." + name).str());
Obligations.insert({Obligation::Key::forMember(key),
{p.getFirst().second, Expectation::Kind::Member,
p.getSecond() ? Expectation::Flavor::Cascading
: Expectation::Flavor::Private,
nominalDeclContext->getLoc()}});
{context, Expectation::Kind::Member,
isCascadingUse ? Expectation::Scope::Cascading
: Expectation::Scope::Private}});
}
break;
case NodeKind::dynamicLookup:
Obligations.insert({Obligation::Key::forDynamicMember(name),
{context, Expectation::Kind::DynamicMember,
isCascadingUse ? Expectation::Scope::Cascading
: Expectation::Scope::Private}});
break;
case NodeKind::topLevel:
case NodeKind::sourceFileProvide:
Obligations.insert({Obligation::Key::forProvides(name),
{name, Expectation::Kind::Provides,
Expectation::Scope::None}});
break;
case NodeKind::kindCount:
llvm_unreachable("Given count node?");
}
});
return false;
}
@@ -482,7 +486,7 @@ bool DependencyVerifier::verifyObligations(
for (auto &expectation : ExpectedDependencies) {
const bool wantsCascade = expectation.isCascading();
switch (expectation.Info.first) {
switch (expectation.Info.Kind) {
case Expectation::Kind::Negative:
// We'll verify negative expectations separately.
NegativeExpectations.insert({expectation.MessageRange, expectation});
@@ -529,7 +533,7 @@ bool DependencyVerifier::verifyObligations(
},
[this](const Expectation &e) {
addFormattedDiagnostic(
e, "expected superclass dependency does not exist: {0}",
e, "expected potential member dependency does not exist: {0}",
e.MessageRange);
});
break;
@@ -559,7 +563,7 @@ bool DependencyVerifier::verifyObligations(
bool DependencyVerifier::verifyNegativeExpectations(
ObligationMap &Obligations, NegativeExpectationMap &NegativeExpectations) {
forEachActiveObligation(Obligations, [&](StringRef key, Obligation &p) {
forEachOwedObligation(Obligations, [&](StringRef key, Obligation &p) {
auto entry = NegativeExpectations.find(key);
if (entry == NegativeExpectations.end()) {
return;
@@ -577,19 +581,11 @@ bool DependencyVerifier::diagnoseUnfulfilledObligations(
const SourceFile *SF, ObligationMap &Obligations) {
CharSourceRange EntireRange = SM.getRangeForBuffer(*SF->getBufferID());
StringRef InputFile = SM.extractText(EntireRange);
forEachActiveObligation(Obligations, [&](StringRef key, Obligation &p) {
const char *Loc = NULL;
if (p.getLocation().isValid()) {
// If we have a recommended location, try to use it.
Loc = (const char *)p.getLocation().getOpaquePointerValue();
} else {
// Otherwise diagnose the input buffer.
forEachOwedObligation(Obligations, [&](StringRef key, Obligation &p) {
// HACK: Diagnosing the end of the buffer will print a carat pointing
// at the file path, but not print any of the buffer's contents, which
// might be misleading.
Loc = InputFile.end();
}
const char *Loc = InputFile.end();
switch (p.getKind()) {
case Expectation::Kind::Negative:
llvm_unreachable("Obligations may not be negative; only Expectations!");
@@ -600,7 +596,7 @@ bool DependencyVerifier::diagnoseUnfulfilledObligations(
case Expectation::Kind::DynamicMember:
addFormattedDiagnostic(Loc,
"unexpected {0} dynamic member dependency: {1}",
p.describeCascade(), p.getName().userFacingName());
p.describeCascade(), p.getName());
break;
case Expectation::Kind::PotentialMember:
addFormattedDiagnostic(Loc,
@@ -609,7 +605,7 @@ bool DependencyVerifier::diagnoseUnfulfilledObligations(
break;
case Expectation::Kind::Provides:
addFormattedDiagnostic(Loc, "unexpected provided entity: {0}",
p.getName().userFacingName());
p.getName());
break;
}
});
@@ -655,12 +651,13 @@ bool DependencyVerifier::verifyFile(const SourceFile *SF) {
}
//===----------------------------------------------------------------------===//
// Main entrypoints
// MARK: Main entrypoints
//===----------------------------------------------------------------------===//
bool swift::verifyDependencies(SourceManager &SM, ArrayRef<FileUnit *> SFs) {
bool swift::verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
ArrayRef<FileUnit *> SFs) {
bool HadError = false;
DependencyVerifier Verifier{SM};
DependencyVerifier Verifier{SM, DT};
for (const auto *FU : SFs) {
if (const auto *SF = dyn_cast<SourceFile>(FU))
HadError |= Verifier.verifyFile(SF);
@@ -668,9 +665,10 @@ bool swift::verifyDependencies(SourceManager &SM, ArrayRef<FileUnit *> SFs) {
return HadError;
}
bool swift::verifyDependencies(SourceManager &SM, ArrayRef<SourceFile *> SFs) {
bool swift::verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
ArrayRef<SourceFile *> SFs) {
bool HadError = false;
DependencyVerifier Verifier{SM};
DependencyVerifier Verifier{SM, DT};
for (const auto *SF : SFs) {
HadError |= Verifier.verifyFile(SF);
}

View File

@@ -1482,7 +1482,7 @@ computeDeallocatableResources(const CompilerInvocation &Invocation,
// Verifying incremental dependencies relies on access to the Swift Module's
// source files. We can still free the SIL module, though.
if (Invocation.getFrontendOptions().VerifyDependencies) {
if (Invocation.getFrontendOptions().EnableIncrementalDependencyVerifier) {
return DeallocatableResources::SILModule;
}
@@ -2199,12 +2199,14 @@ int swift::performFrontend(ArrayRef<const char *> Args,
// Verify reference dependencies of the current compilation job *before*
// verifying diagnostics so that the former can be tested via the latter.
if (Invocation.getFrontendOptions().VerifyDependencies) {
if (Invocation.getFrontendOptions().EnableIncrementalDependencyVerifier) {
if (!Instance->getPrimarySourceFiles().empty()) {
HadError |= swift::verifyDependencies(Instance->getSourceMgr(),
*Instance->getDependencyTracker(),
Instance->getPrimarySourceFiles());
} else {
HadError |= swift::verifyDependencies(Instance->getSourceMgr(),
HadError |= swift::verifyDependencies(
Instance->getSourceMgr(), *Instance->getDependencyTracker(),
Instance->getMainModule()->getFiles());
}
}

View File

@@ -1,21 +1,15 @@
// UNSUPPORTED: OS=windows-msvc
// 'find' trick is non-portable...
// RUN: %empty-directory(%t)
// RUN: %{python} %S/../gen-output-file-map.py -o %t %S/Inputs
// RUN: find %S -name "*.swift" > %t/Sources.resp
// RUN: %{python} %S/../gen-output-file-map.py -o %t %S/Inputs -r %t.resp
// RUN: cat %t.resp
// RUN: cd %t
// RUN: not %target-swiftc_driver -no-color-diagnostics -typecheck -output-file-map %t/output.json -incremental -module-name main -verify-incremental-dependencies @%t/Sources.resp 2>&1 | %FileCheck %s
// RUN: not %target-swiftc_driver -no-color-diagnostics -typecheck -output-file-map %t/output.json -incremental -module-name main -verify-incremental-dependencies @%t.resp 2>&1 | sort | %FileCheck %s
// N.B. We use CHECK-DAG so we aren't subject to ordering problems caused by the
// driver choosing to reschedule different frontend processes, and various
// terminal emulators choosing to buffer rerouted file descriptors differently.
// CHECK-DAG: unexpected cascading dependency: main.Subclass.init
// CHECK-DAG: unexpected cascading dependency: main.Subclass.deinit
// CHECK-DAG: unexpected provided entity: PublicProtocol
// CHECK-DAG: unexpected provided entity: BaseProtocol
// CHECK-DAG: unexpected provided entity: Base
// CHECK-DAG: unexpected provided entity: Subclass
// CHECK-DAG: unexpected cascading dependency: main.Base.init
// CHECK-DAG: unexpected dependency exists: main.BaseProtocol
// CHECK: unexpected dependency exists: main.BaseProtocol
// CHECK: unexpected cascading dependency: main.Base.init
// CHECK: unexpected cascading dependency: main.Subclass.deinit
// CHECK: unexpected cascading dependency: main.Subclass.init
// CHECK: unexpected cascading potential member dependency: main.Base
// CHECK: unexpected provided entity: Base
// CHECK: unexpected provided entity: BaseProtocol
// CHECK: unexpected provided entity: PublicProtocol
// CHECK: unexpected provided entity: Subclass

View File

@@ -18,7 +18,7 @@ def find_swift_files(path):
for filename in files:
if not filename.endswith('.swift'):
continue
yield filename
yield (parent, filename)
def main(arguments):
@@ -26,6 +26,9 @@ def main(arguments):
description='Generate an output file map for the given directory')
parser.add_argument('-o', dest='output_dir',
help='Directory to which the file map will be emitted')
parser.add_argument('-r', dest='response_output_file',
help="""Directory to which a matching response file
will be emitted""")
parser.add_argument('input_dir', help='a directory of swift files')
args = parser.parse_args(arguments)
@@ -45,13 +48,15 @@ def main(arguments):
if not swift_files:
fatal("no swift files in the given input directory")
response_file_contents = []
all_records = {}
for swift_file in swift_files:
for (root, swift_file) in swift_files:
file_name = os.path.splitext(swift_file)[0]
all_records['./' + swift_file] = {
'object': './' + file_name + '.o',
'swift-dependencies': './' + file_name + '.swiftdeps',
}
response_file_contents.append(os.path.join(root, swift_file))
all_records[""] = {
'swift-dependencies': './main-buildrecord.swiftdeps'
}
@@ -59,6 +64,11 @@ def main(arguments):
with open(output_path, 'w') as f:
json.dump(all_records, f)
if args.response_output_file is not None:
with open(args.response_output_file, 'w') as f:
for line in response_file_contents:
f.write(line + " ")
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@@ -1,11 +1,7 @@
// UNSUPPORTED: OS=windows-msvc
// 'find' trick is non-portable...
// RUN: %empty-directory(%t)
// RUN: %{python} %S/../gen-output-file-map.py -o %t %S/Inputs
// RUN: find %S -name "*.swift" > %t/Sources.resp
// RUN: cd %t && %target-swiftc_driver -typecheck -output-file-map %t/output.json -incremental -module-name main -verify-incremental-dependencies @%t/Sources.resp
// RUN: cd %t && %target-swiftc_driver -typecheck -output-file-map %t/output.json -incremental -enable-batch-mode -module-name main -verify-incremental-dependencies @%t/Sources.resp
// RUN: %{python} %S/../gen-output-file-map.py -o %t %S/Inputs -r %t.resp
// RUN: cd %t && %target-swiftc_driver -typecheck -output-file-map %t/output.json -incremental -module-name main -verify-incremental-dependencies @%t.resp
// RUN: cd %t && %target-swiftc_driver -typecheck -output-file-map %t/output.json -incremental -enable-batch-mode -module-name main -verify-incremental-dependencies @%t.resp
// N.B. These tests are meant to continue to expand to more and more input files
// as more kinds of cross-type dependencies are discovered. This will naturally

View File

@@ -8,27 +8,26 @@ import Foundation
// expected-provides {{LookupFactory}}
// expected-provides {{NSObject}}
// expected-private-superclass {{ObjectiveC.NSObject}}
// expected-private-superclass {{__C.NSObject}}
// expected-private-conformance {{Foundation._KeyValueCodingAndObserving}}
// expected-private-conformance {{Swift.Hashable}}
// expected-private-conformance {{Swift.Equatable}}
// expected-private-conformance {{Swift.CustomDebugStringConvertible}}
// expected-private-conformance {{Swift.CVarArg}}
// expected-private-conformance {{ObjectiveC.NSObjectProtocol}}
// expected-private-conformance {{Swift.CustomStringConvertible}}
@objc private class LookupFactory: NSObject {
// expected-provides {{AssignmentPrecedence}}
// expected-provides {{IntegerLiteralType}}
// expected-provides {{FloatLiteralType}}
// expected-provides {{Int}}
// expected-cascading-member {{ObjectiveC.NSObject.someMember}}
// expected-cascading-member {{ObjectiveC.NSObject.Int}}
// expected-cascading-member {{__C.NSObject.someMember}}
// expected-cascading-member {{__C.NSObject.Int}}
// expected-cascading-member {{main.LookupFactory.Int}}
@objc var someMember: Int = 0
// expected-cascading-member {{ObjectiveC.NSObject.someMethod}}
// expected-cascading-member {{__C.NSObject.someMethod}}
@objc func someMethod() {}
// expected-cascading-member {{ObjectiveC.NSObject.init}}
// expected-cascading-member {{__C.NSObject.init}}
// expected-cascading-member {{main.LookupFactory.init}}
// expected-cascading-member {{main.LookupFactory.deinit}}
// expected-cascading-member {{main.LookupFactory.someMember}}