Skip to content

[Batch mode] Merge pull request #23735 -rdar-40167351 #23848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 42 additions & 21 deletions include/swift/AST/DiagnosticConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class DiagnosticConsumer {

public:
virtual ~DiagnosticConsumer();

/// Invoked whenever the frontend emits a diagnostic.
///
/// \param SM The source manager associated with the source locations in
Expand All @@ -94,11 +94,20 @@ class DiagnosticConsumer {
/// \param FormatArgs The diagnostic format string arguments.
///
/// \param Info Extra information associated with the diagnostic.
virtual void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) = 0;
///
/// \param bufferIndirectlyCausingDiagnostic Only used when directing
/// diagnostics to different outputs.
/// In batch mode a diagnostic may be
/// located in a non-primary file, but there will be no .dia file for a
/// non-primary. If valid, this argument contains a location within a buffer
/// that corresponds to a primary input. The .dia file for that primary can be
/// used for the diagnostic, as if it had occurred at this location.
virtual void
handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) = 0;

/// \returns true if an error occurred while finishing-up.
virtual bool finishProcessing() { return false; }
Expand All @@ -116,11 +125,11 @@ class DiagnosticConsumer {
/// DiagnosticConsumer that discards all diagnostics.
class NullDiagnosticConsumer : public DiagnosticConsumer {
public:
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) override;
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) override;
};

/// DiagnosticConsumer that forwards diagnostics to the consumers of
Expand All @@ -129,11 +138,11 @@ class ForwardingDiagnosticConsumer : public DiagnosticConsumer {
DiagnosticEngine &TargetEngine;
public:
ForwardingDiagnosticConsumer(DiagnosticEngine &Target);
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) override;
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) override;
};

/// DiagnosticConsumer that funnels diagnostics in certain files to
Expand Down Expand Up @@ -175,8 +184,12 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
std::string inputFileName;

/// The consumer (if any) for diagnostics associated with the inputFileName.
/// A null pointer for the DiagnosticConsumer means that diagnostics for
/// this file should not be emitted.
/// A null pointer for the DiagnosticConsumer means that this file is a
/// non-primary one in batch mode and we have no .dia file for it.
/// If there is a responsible primary when the diagnostic is handled
/// it will be shunted to that primary's .dia file.
/// Otherwise it will be suppressed, assuming that the diagnostic will
/// surface in another frontend job that compiles that file as a primary.
std::unique_ptr<DiagnosticConsumer> consumer;

// Has this subconsumer ever handled a diagnostic that is an error?
Expand All @@ -191,16 +204,16 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
std::unique_ptr<DiagnosticConsumer> consumer)
: inputFileName(inputFileName), consumer(std::move(consumer)) {}

void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
const DiagnosticInfo &Info,
const SourceLoc bufferIndirectlyCausingDiagnostic) {
if (!getConsumer())
return;
hasAnErrorBeenConsumed |= Kind == DiagnosticKind::Error;
getConsumer()->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs,
Info);
Info, bufferIndirectlyCausingDiagnostic);
}

void informDriverOfIncompleteBatchModeCompilation() {
Expand Down Expand Up @@ -287,11 +300,11 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
SmallVectorImpl<Subconsumer> &consumers);

public:
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) override;
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) override;

bool finishProcessing() override;

Expand All @@ -309,6 +322,14 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
/// a particular consumer if diagnostic goes there.
Optional<FileSpecificDiagnosticConsumer::Subconsumer *>
subconsumerForLocation(SourceManager &SM, SourceLoc loc);

Optional<FileSpecificDiagnosticConsumer::Subconsumer *>
findSubconsumer(SourceManager &SM, SourceLoc loc, DiagnosticKind Kind,
SourceLoc bufferIndirectlyCausingDiagnostic);

Optional<FileSpecificDiagnosticConsumer::Subconsumer *>
findSubconsumerForNonNote(SourceManager &SM, SourceLoc loc,
SourceLoc bufferIndirectlyCausingDiagnostic);
};

} // end namespace swift
Expand Down
30 changes: 29 additions & 1 deletion include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ namespace swift {
class DiagnosticEngine;
class SourceManager;
class ValueDecl;

class SourceFile;

enum class PatternKind : uint8_t;
enum class ReferenceOwnership : uint8_t;
enum class StaticSpellingKind : uint8_t;
Expand Down Expand Up @@ -549,6 +550,12 @@ namespace swift {
/// emitted once all transactions have closed.
unsigned TransactionCount = 0;

/// For batch mode, use this to know where to output a diagnostic from a
/// non-primary file. It's any location in the buffer of the current primary
/// input being compiled.
/// May be invalid.
SourceLoc bufferIndirectlyCausingDiagnostic;

friend class InFlightDiagnostic;
friend class DiagnosticTransaction;

Expand Down Expand Up @@ -787,6 +794,27 @@ namespace swift {

public:
static const char *diagnosticStringFor(const DiagID id);

/// If there is no clear .dia file for a diagnostic, put it in the one
/// corresponding to the SourceLoc given here.
/// In particular, in batch mode when a diagnostic is located in
/// a non-primary file, use this affordance to place it in the .dia
/// file for the primary that is currently being worked on.
void setBufferIndirectlyCausingDiagnosticToInput(SourceLoc);
void resetBufferIndirectlyCausingDiagnostic();
SourceLoc getDefaultDiagnosticLoc() const {
return bufferIndirectlyCausingDiagnostic;
}
};

class BufferIndirectlyCausingDiagnosticRAII {
private:
DiagnosticEngine &Diags;
public:
BufferIndirectlyCausingDiagnosticRAII(const SourceFile &SF);
~BufferIndirectlyCausingDiagnosticRAII() {
Diags.resetBufferIndirectlyCausingDiagnostic();
}
};

/// Represents a diagnostic transaction. While a transaction is
Expand Down
11 changes: 6 additions & 5 deletions include/swift/Frontend/PrintingDiagnosticConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer {
PrintingDiagnosticConsumer(llvm::raw_ostream &stream = llvm::errs()) :
Stream(stream) { }

virtual void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) override;
virtual void
handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) override;

void forceColors() {
ForceColors = true;
Expand Down
6 changes: 3 additions & 3 deletions include/swift/Migrator/FixitApplyDiagnosticConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ class FixitApplyDiagnosticConsumer final
/// output stream.
void printResult(llvm::raw_ostream &OS) const;

void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
DiagnosticKind Kind,
void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString,
ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) override;
const DiagnosticInfo &Info,
SourceLoc bufferIndirectlyCausingDiagnostic) override;

unsigned getNumFixitsApplied() const {
return NumFixitsApplied;
Expand Down
75 changes: 58 additions & 17 deletions lib/AST/DiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ FileSpecificDiagnosticConsumer::subconsumerForLocation(SourceManager &SM,
if (loc.isInvalid())
return None;

// What if a there's a FileSpecificDiagnosticConsumer but there are no
// subconsumers in it? (This situation occurs for the fix-its
// FileSpecificDiagnosticConsumer.) In such a case, bail out now.
if (Subconsumers.empty())
return None;

// This map is generated on first use and cached, to allow the
// FileSpecificDiagnosticConsumer to be set up before the source files are
// actually loaded.
Expand Down Expand Up @@ -165,29 +171,62 @@ FileSpecificDiagnosticConsumer::subconsumerForLocation(SourceManager &SM,
void FileSpecificDiagnosticConsumer::handleDiagnostic(
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
const DiagnosticInfo &Info,
const SourceLoc bufferIndirectlyCausingDiagnostic) {

HasAnErrorBeenConsumed |= Kind == DiagnosticKind::Error;

Optional<FileSpecificDiagnosticConsumer::Subconsumer *> subconsumer;
auto subconsumer =
findSubconsumer(SM, Loc, Kind, bufferIndirectlyCausingDiagnostic);
if (subconsumer) {
subconsumer.getValue()->handleDiagnostic(SM, Loc, Kind, FormatString,
FormatArgs, Info,
bufferIndirectlyCausingDiagnostic);
return;
}
// Last resort: spray it everywhere
for (auto &subconsumer : Subconsumers)
subconsumer.handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info,
bufferIndirectlyCausingDiagnostic);
}

Optional<FileSpecificDiagnosticConsumer::Subconsumer *>
FileSpecificDiagnosticConsumer::findSubconsumer(
SourceManager &SM, SourceLoc loc, DiagnosticKind Kind,
SourceLoc bufferIndirectlyCausingDiagnostic) {
// Ensure that a note goes to the same place as the preceeding non-note.
switch (Kind) {
case DiagnosticKind::Error:
case DiagnosticKind::Warning:
case DiagnosticKind::Remark:
subconsumer = subconsumerForLocation(SM, Loc);
case DiagnosticKind::Remark: {
auto subconsumer =
findSubconsumerForNonNote(SM, loc, bufferIndirectlyCausingDiagnostic);
SubconsumerForSubsequentNotes = subconsumer;
break;
case DiagnosticKind::Note:
subconsumer = SubconsumerForSubsequentNotes;
break;
return subconsumer;
}
if (subconsumer.hasValue()) {
subconsumer.getValue()->handleDiagnostic(SM, Loc, Kind, FormatString,
FormatArgs, Info);
return;
case DiagnosticKind::Note:
return SubconsumerForSubsequentNotes;
}
for (auto &subconsumer : Subconsumers)
subconsumer.handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info);
}

Optional<FileSpecificDiagnosticConsumer::Subconsumer *>
FileSpecificDiagnosticConsumer::findSubconsumerForNonNote(
SourceManager &SM, const SourceLoc loc,
const SourceLoc bufferIndirectlyCausingDiagnostic) {
const auto subconsumer = subconsumerForLocation(SM, loc);
if (!subconsumer)
return None; // No place to put it; might be in an imported module
if ((*subconsumer)->getConsumer())
return subconsumer; // A primary file with a .dia file
// Try to put it in the responsible primary input
if (bufferIndirectlyCausingDiagnostic.isInvalid())
return None;
const auto currentPrimarySubconsumer =
subconsumerForLocation(SM, bufferIndirectlyCausingDiagnostic);
assert(!currentPrimarySubconsumer ||
(*currentPrimarySubconsumer)->getConsumer() &&
"current primary must have a .dia file");
return currentPrimarySubconsumer;
}

bool FileSpecificDiagnosticConsumer::finishProcessing() {
Expand All @@ -214,7 +253,7 @@ void FileSpecificDiagnosticConsumer::
void NullDiagnosticConsumer::handleDiagnostic(
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
const DiagnosticInfo &Info, const SourceLoc) {
LLVM_DEBUG({
llvm::dbgs() << "NullDiagnosticConsumer received diagnostic: ";
DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), FormatString,
Expand All @@ -229,14 +268,16 @@ ForwardingDiagnosticConsumer::ForwardingDiagnosticConsumer(DiagnosticEngine &Tar
void ForwardingDiagnosticConsumer::handleDiagnostic(
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
const DiagnosticInfo &Info,
const SourceLoc bufferIndirectlyCausingDiagnostic) {
LLVM_DEBUG({
llvm::dbgs() << "ForwardingDiagnosticConsumer received diagnostic: ";
DiagnosticEngine::formatDiagnosticText(llvm::dbgs(), FormatString,
FormatArgs);
llvm::dbgs() << "\n";
});
for (auto *C : TargetEngine.getConsumers()) {
C->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info);
C->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info,
bufferIndirectlyCausingDiagnostic);
}
}
30 changes: 29 additions & 1 deletion lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,14 +826,31 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
for (auto &Consumer : Consumers) {
Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior),
diagnosticStringFor(Info.ID),
diagnostic.getArgs(), Info);
diagnostic.getArgs(), Info,
getDefaultDiagnosticLoc());
}
}

const char *DiagnosticEngine::diagnosticStringFor(const DiagID id) {
return diagnosticStrings[(unsigned)id];
}

void DiagnosticEngine::setBufferIndirectlyCausingDiagnosticToInput(
SourceLoc loc) {
// If in the future, nested BufferIndirectlyCausingDiagnosticRAII need be
// supported, the compiler will need a stack for
// bufferIndirectlyCausingDiagnostic.
assert(bufferIndirectlyCausingDiagnostic.isInvalid() &&
"Buffer should not already be set.");
bufferIndirectlyCausingDiagnostic = loc;
assert(bufferIndirectlyCausingDiagnostic.isValid() &&
"Buffer must be valid for previous assertion to work.");
}

void DiagnosticEngine::resetBufferIndirectlyCausingDiagnostic() {
bufferIndirectlyCausingDiagnostic = SourceLoc();
}

DiagnosticSuppression::DiagnosticSuppression(DiagnosticEngine &diags)
: diags(diags)
{
Expand All @@ -844,3 +861,14 @@ DiagnosticSuppression::~DiagnosticSuppression() {
for (auto consumer : consumers)
diags.addConsumer(*consumer);
}

BufferIndirectlyCausingDiagnosticRAII::BufferIndirectlyCausingDiagnosticRAII(
const SourceFile &SF)
: Diags(SF.getASTContext().Diags) {
auto id = SF.getBufferID();
if (!id)
return;
auto loc = SF.getASTContext().SourceMgr.getLocForBufferStart(*id);
if (loc.isValid())
Diags.setBufferIndirectlyCausingDiagnosticToInput(loc);
}
3 changes: 2 additions & 1 deletion lib/Frontend/PrintingDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ namespace {
void PrintingDiagnosticConsumer::handleDiagnostic(
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
const DiagnosticInfo &Info,
const SourceLoc bufferIndirectlyCausingDiagnostic) {
// Determine what kind of diagnostic we're emitting.
llvm::SourceMgr::DiagKind SMKind;
switch (Kind) {
Expand Down
Loading