mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[DiagnosticVerifier] Make Diagnostic Verifier a DiagnosticConsumer subclass
Update DiagnosticVerifier to respect color flags Improve DiagnosticVerifier test coverage verifier updates to support new llvm stable branch
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Copyright (c) 2014 - 2020 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
|
||||
@@ -18,30 +18,87 @@
|
||||
#ifndef SWIFT_FRONTEND_DIAGNOSTIC_VERIFIER_H
|
||||
#define SWIFT_FRONTEND_DIAGNOSTIC_VERIFIER_H
|
||||
|
||||
#include "swift/AST/DiagnosticConsumer.h"
|
||||
#include "swift/Basic/LLVM.h"
|
||||
|
||||
namespace swift {
|
||||
class DependencyTracker;
|
||||
class FileUnit;
|
||||
class SourceManager;
|
||||
class SourceFile;
|
||||
class DependencyTracker;
|
||||
class FileUnit;
|
||||
class SourceManager;
|
||||
class SourceFile;
|
||||
|
||||
/// Set up the specified source manager so that diagnostics are captured
|
||||
/// instead of being printed.
|
||||
void enableDiagnosticVerifier(SourceManager &SM);
|
||||
// MARK: - DependencyVerifier
|
||||
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
|
||||
ArrayRef<FileUnit *> SFs);
|
||||
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
|
||||
ArrayRef<SourceFile *> SFs);
|
||||
|
||||
/// Verify that captured diagnostics meet with the expectations of the source
|
||||
/// files corresponding to the specified \p BufferIDs and tear down our
|
||||
/// support for capturing and verifying diagnostics.
|
||||
///
|
||||
/// This returns true if there are any mismatches found.
|
||||
bool verifyDiagnostics(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
|
||||
bool autoApplyFixes, bool ignoreUnknown);
|
||||
// MARK: - DiagnosticVerifier
|
||||
struct ExpectedFixIt;
|
||||
|
||||
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
|
||||
ArrayRef<FileUnit *> SFs);
|
||||
bool verifyDependencies(SourceManager &SM, const DependencyTracker &DT,
|
||||
ArrayRef<SourceFile *> SFs);
|
||||
struct CapturedDiagnosticInfo {
|
||||
llvm::SmallString<128> Message;
|
||||
llvm::SmallString<32> FileName;
|
||||
DiagnosticKind Classification;
|
||||
SourceLoc Loc;
|
||||
unsigned Line;
|
||||
unsigned Column;
|
||||
SmallVector<DiagnosticInfo::FixIt, 2> FixIts;
|
||||
|
||||
CapturedDiagnosticInfo(llvm::SmallString<128> Message,
|
||||
llvm::SmallString<32> FileName,
|
||||
DiagnosticKind Classification, SourceLoc Loc,
|
||||
unsigned Line, unsigned Column,
|
||||
SmallVector<DiagnosticInfo::FixIt, 2> FixIts)
|
||||
: Message(Message), FileName(FileName), Classification(Classification),
|
||||
Loc(Loc), Line(Line), Column(Column), FixIts(FixIts) {}
|
||||
};
|
||||
/// This class implements support for -verify mode in the compiler. It
|
||||
/// buffers up diagnostics produced during compilation, then checks them
|
||||
/// against expected-error markers in the source file.
|
||||
class DiagnosticVerifier : public DiagnosticConsumer {
|
||||
SourceManager &SM;
|
||||
std::vector<CapturedDiagnosticInfo> CapturedDiagnostics;
|
||||
ArrayRef<unsigned> BufferIDs;
|
||||
bool AutoApplyFixes;
|
||||
bool IgnoreUnknown;
|
||||
|
||||
public:
|
||||
explicit DiagnosticVerifier(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
|
||||
bool AutoApplyFixes, bool IgnoreUnknown)
|
||||
: SM(SM), BufferIDs(BufferIDs), AutoApplyFixes(AutoApplyFixes),
|
||||
IgnoreUnknown(IgnoreUnknown) {}
|
||||
|
||||
virtual void handleDiagnostic(SourceManager &SM,
|
||||
const DiagnosticInfo &Info) override;
|
||||
|
||||
virtual bool finishProcessing() override;
|
||||
|
||||
private:
|
||||
/// Result of verifying a file.
|
||||
struct Result {
|
||||
/// Were there any errors? All of the following are considered errors:
|
||||
/// - Expected diagnostics that were not present
|
||||
/// - Unexpected diagnostics that were present
|
||||
/// - Errors in the definition of expected diagnostics
|
||||
bool HadError;
|
||||
bool HadUnexpectedDiag;
|
||||
};
|
||||
|
||||
/// verifyFile - After the file has been processed, check to see if we
|
||||
/// got all of the expected diagnostics and check to see if there were any
|
||||
/// unexpected ones.
|
||||
Result verifyFile(unsigned BufferID);
|
||||
|
||||
bool checkForFixIt(const ExpectedFixIt &Expected,
|
||||
const CapturedDiagnosticInfo &D, StringRef buffer);
|
||||
|
||||
// Render the verifier syntax for a given set of fix-its.
|
||||
std::string renderFixits(ArrayRef<DiagnosticInfo::FixIt> fixits,
|
||||
StringRef InputFile);
|
||||
|
||||
void printRemainingDiagnostics() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "swift/Basic/SourceManager.h"
|
||||
#include "swift/ClangImporter/ClangImporter.h"
|
||||
#include "swift/ClangImporter/ClangImporterOptions.h"
|
||||
#include "swift/Frontend/DiagnosticVerifier.h"
|
||||
#include "swift/Frontend/FrontendOptions.h"
|
||||
#include "swift/Frontend/ModuleInterfaceSupport.h"
|
||||
#include "swift/Migrator/MigratorOptions.h"
|
||||
@@ -405,6 +406,7 @@ class CompilerInstance {
|
||||
std::unique_ptr<ASTContext> Context;
|
||||
std::unique_ptr<Lowering::TypeConverter> TheSILTypes;
|
||||
std::unique_ptr<SILModule> TheSILModule;
|
||||
std::unique_ptr<DiagnosticVerifier> DiagVerifier;
|
||||
|
||||
/// Null if no tracker.
|
||||
std::unique_ptr<DependencyTracker> DepTracker;
|
||||
@@ -585,6 +587,7 @@ private:
|
||||
bool setUpInputs();
|
||||
bool setUpASTContextIfNeeded();
|
||||
void setupStatsReporter();
|
||||
void setupDiagnosticVerifierIfNeeded();
|
||||
Optional<unsigned> setUpCodeCompletionBuffer();
|
||||
|
||||
/// Set up all state in the CompilerInstance to process the given input file.
|
||||
|
||||
@@ -40,6 +40,7 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer {
|
||||
// Educational notes which are buffered until the consumer is finished
|
||||
// constructing a snippet.
|
||||
SmallVector<std::string, 1> BufferedEducationalNotes;
|
||||
bool SuppressOutput = false;
|
||||
|
||||
public:
|
||||
PrintingDiagnosticConsumer(llvm::raw_ostream &stream = llvm::errs());
|
||||
@@ -65,6 +66,10 @@ public:
|
||||
return DidErrorOccur;
|
||||
}
|
||||
|
||||
void setSuppressOutput(bool suppressOutput) {
|
||||
SuppressOutput = suppressOutput;
|
||||
}
|
||||
|
||||
private:
|
||||
void printDiagnostic(SourceManager &SM, const DiagnosticInfo &Info);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Copyright (c) 2014 - 2020 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
|
||||
@@ -24,132 +24,79 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
namespace swift {
|
||||
struct ExpectedFixIt {
|
||||
const char *StartLoc, *EndLoc; // The loc of the {{ and }}'s.
|
||||
unsigned StartCol;
|
||||
unsigned EndCol;
|
||||
std::string Text;
|
||||
};
|
||||
} // end namespace swift
|
||||
|
||||
namespace {
|
||||
struct ExpectedFixIt {
|
||||
const char *StartLoc, *EndLoc; // The loc of the {{ and }}'s.
|
||||
unsigned StartCol;
|
||||
unsigned EndCol;
|
||||
std::string Text;
|
||||
};
|
||||
struct ExpectedDiagnosticInfo {
|
||||
// This specifies the full range of the "expected-foo {{}}" specifier.
|
||||
const char *ExpectedStart, *ExpectedEnd = nullptr;
|
||||
|
||||
struct ExpectedDiagnosticInfo {
|
||||
// This specifies the full range of the "expected-foo {{}}" specifier.
|
||||
const char *ExpectedStart, *ExpectedEnd = nullptr;
|
||||
DiagnosticKind Classification;
|
||||
|
||||
llvm::SourceMgr::DiagKind Classification;
|
||||
|
||||
// This is true if a '*' constraint is present to say that the diagnostic
|
||||
// may appear (or not) an uncounted number of times.
|
||||
bool mayAppear = false;
|
||||
|
||||
// This is true if a '{{none}}' is present to mark that there should be no
|
||||
// extra fixits.
|
||||
bool noExtraFixitsMayAppear = false;
|
||||
// This is true if a '*' constraint is present to say that the diagnostic
|
||||
// may appear (or not) an uncounted number of times.
|
||||
bool mayAppear = false;
|
||||
|
||||
// This is the raw input buffer for the message text, the part in the
|
||||
// {{...}}
|
||||
StringRef MessageRange;
|
||||
|
||||
// This is the message string with escapes expanded.
|
||||
std::string MessageStr;
|
||||
unsigned LineNo = ~0U;
|
||||
Optional<unsigned> ColumnNo;
|
||||
// This is true if a '{{none}}' is present to mark that there should be no
|
||||
// extra fixits.
|
||||
bool noExtraFixitsMayAppear = false;
|
||||
|
||||
std::vector<ExpectedFixIt> Fixits;
|
||||
// This is the raw input buffer for the message text, the part in the
|
||||
// {{...}}
|
||||
StringRef MessageRange;
|
||||
|
||||
ExpectedDiagnosticInfo(const char *ExpectedStart,
|
||||
llvm::SourceMgr::DiagKind Classification)
|
||||
: ExpectedStart(ExpectedStart), Classification(Classification) {
|
||||
}
|
||||
|
||||
};
|
||||
} // end anonymous namespace
|
||||
// This is the message string with escapes expanded.
|
||||
std::string MessageStr;
|
||||
unsigned LineNo = ~0U;
|
||||
Optional<unsigned> ColumnNo;
|
||||
|
||||
static std::string getDiagKindString(llvm::SourceMgr::DiagKind Kind) {
|
||||
std::vector<ExpectedFixIt> Fixits;
|
||||
|
||||
ExpectedDiagnosticInfo(const char *ExpectedStart,
|
||||
DiagnosticKind Classification)
|
||||
: ExpectedStart(ExpectedStart), Classification(Classification) {}
|
||||
};
|
||||
|
||||
static std::string getDiagKindString(DiagnosticKind Kind) {
|
||||
switch (Kind) {
|
||||
case llvm::SourceMgr::DK_Error: return "error";
|
||||
case llvm::SourceMgr::DK_Warning: return "warning";
|
||||
case llvm::SourceMgr::DK_Note: return "note";
|
||||
case llvm::SourceMgr::DK_Remark: return "remark";
|
||||
case DiagnosticKind::Error:
|
||||
return "error";
|
||||
case DiagnosticKind::Warning:
|
||||
return "warning";
|
||||
case DiagnosticKind::Note:
|
||||
return "note";
|
||||
case DiagnosticKind::Remark:
|
||||
return "remark";
|
||||
}
|
||||
|
||||
llvm_unreachable("Unhandled DiagKind in switch.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
/// This class implements support for -verify mode in the compiler. It
|
||||
/// buffers up diagnostics produced during compilation, then checks them
|
||||
/// against expected-error markers in the source file.
|
||||
class DiagnosticVerifier {
|
||||
SourceManager &SM;
|
||||
std::vector<llvm::SMDiagnostic> CapturedDiagnostics;
|
||||
public:
|
||||
explicit DiagnosticVerifier(SourceManager &SM) : SM(SM) {}
|
||||
|
||||
void addDiagnostic(const llvm::SMDiagnostic &Diag) {
|
||||
CapturedDiagnostics.push_back(Diag);
|
||||
}
|
||||
|
||||
/// Result of verifying a file.
|
||||
struct Result {
|
||||
/// Were there any errors? All of the following are considered errors:
|
||||
/// - Expected diagnostics that were not present
|
||||
/// - Unexpected diagnostics that were present
|
||||
/// - Errors in the definition of expected diagnostics
|
||||
bool HadError;
|
||||
bool HadUnexpectedDiag;
|
||||
};
|
||||
|
||||
/// verifyFile - After the file has been processed, check to see if we
|
||||
/// got all of the expected diagnostics and check to see if there were any
|
||||
/// unexpected ones.
|
||||
Result verifyFile(unsigned BufferID, bool autoApplyFixes);
|
||||
|
||||
/// diagnostics for '<unknown>:0' should be considered as unexpected.
|
||||
bool verifyUnknown();
|
||||
|
||||
void printRemainingDiagnostics() const;
|
||||
|
||||
/// If there are any -verify errors (e.g. differences between expectations
|
||||
/// and actual diagnostics produced), apply fixits to the original source
|
||||
/// file and drop it back in place.
|
||||
void autoApplyFixes(unsigned BufferID,
|
||||
ArrayRef<llvm::SMDiagnostic> diagnostics);
|
||||
|
||||
private:
|
||||
std::vector<llvm::SMDiagnostic>::iterator
|
||||
findDiagnostic(const ExpectedDiagnosticInfo &Expected,
|
||||
StringRef BufferName);
|
||||
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
/// If we find the specified diagnostic in the list, return it.
|
||||
/// Otherwise return CapturedDiagnostics.end().
|
||||
std::vector<llvm::SMDiagnostic>::iterator
|
||||
DiagnosticVerifier::findDiagnostic(const ExpectedDiagnosticInfo &Expected,
|
||||
StringRef BufferName) {
|
||||
static std::vector<CapturedDiagnosticInfo>::iterator
|
||||
findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
|
||||
const ExpectedDiagnosticInfo &Expected, StringRef BufferName) {
|
||||
for (auto I = CapturedDiagnostics.begin(), E = CapturedDiagnostics.end();
|
||||
I != E; ++I) {
|
||||
// Verify the file and line of the diagnostic.
|
||||
if (I->getLineNo() != (int)Expected.LineNo ||
|
||||
I->getFilename() != BufferName)
|
||||
if (I->Line != Expected.LineNo || I->FileName != BufferName)
|
||||
continue;
|
||||
|
||||
// If a specific column was expected, verify it. Add one to the captured
|
||||
// index so expected column numbers correspond to printed output.
|
||||
if (Expected.ColumnNo.hasValue() &&
|
||||
I->getColumnNo() + 1 != (int)*Expected.ColumnNo)
|
||||
// If a specific column was expected, verify it.
|
||||
if (Expected.ColumnNo.hasValue() && I->Column != *Expected.ColumnNo)
|
||||
continue;
|
||||
|
||||
// Verify the classification and string.
|
||||
if (I->getKind() != Expected.Classification ||
|
||||
I->getMessage().find(Expected.MessageStr) == StringRef::npos)
|
||||
if (I->Classification != Expected.Classification ||
|
||||
I->Message.find(Expected.MessageStr) == StringRef::npos)
|
||||
continue;
|
||||
|
||||
// Okay, we found a match, hurray!
|
||||
@@ -159,6 +106,105 @@ DiagnosticVerifier::findDiagnostic(const ExpectedDiagnosticInfo &Expected,
|
||||
return CapturedDiagnostics.end();
|
||||
}
|
||||
|
||||
/// If there are any -verify errors (e.g. differences between expectations
|
||||
/// and actual diagnostics produced), apply fixits to the original source
|
||||
/// file and drop it back in place.
|
||||
static void autoApplyFixes(SourceManager &SM, unsigned BufferID,
|
||||
ArrayRef<llvm::SMDiagnostic> diags) {
|
||||
// Walk the list of diagnostics, pulling out any fixits into an array of just
|
||||
// them.
|
||||
SmallVector<llvm::SMFixIt, 4> FixIts;
|
||||
for (auto &diag : diags)
|
||||
FixIts.append(diag.getFixIts().begin(), diag.getFixIts().end());
|
||||
|
||||
// If we have no fixits to apply, avoid touching the file.
|
||||
if (FixIts.empty())
|
||||
return;
|
||||
|
||||
// Sort the fixits by their start location.
|
||||
std::sort(FixIts.begin(), FixIts.end(),
|
||||
[&](const llvm::SMFixIt &lhs, const llvm::SMFixIt &rhs) -> bool {
|
||||
return lhs.getRange().Start.getPointer() <
|
||||
rhs.getRange().Start.getPointer();
|
||||
});
|
||||
// Coalesce identical fix-its. This happens most often with "expected-error 2"
|
||||
// syntax.
|
||||
FixIts.erase(std::unique(FixIts.begin(), FixIts.end(),
|
||||
[](const llvm::SMFixIt &lhs,
|
||||
const llvm::SMFixIt &rhs) -> bool {
|
||||
return lhs.getRange().Start ==
|
||||
rhs.getRange().Start &&
|
||||
lhs.getRange().End == rhs.getRange().End &&
|
||||
lhs.getText() == rhs.getText();
|
||||
}),
|
||||
FixIts.end());
|
||||
// Filter out overlapping fix-its. This allows the compiler to apply changes
|
||||
// to the easy parts of the file, and leave in the tricky cases for the
|
||||
// developer to handle manually.
|
||||
FixIts.erase(swift::removeAdjacentIf(
|
||||
FixIts.begin(), FixIts.end(),
|
||||
[](const llvm::SMFixIt &lhs, const llvm::SMFixIt &rhs) {
|
||||
return lhs.getRange().End.getPointer() >
|
||||
rhs.getRange().Start.getPointer();
|
||||
}),
|
||||
FixIts.end());
|
||||
|
||||
// Get the contents of the original source file.
|
||||
auto memBuffer = SM.getLLVMSourceMgr().getMemoryBuffer(BufferID);
|
||||
auto bufferRange = memBuffer->getBuffer();
|
||||
|
||||
// Apply the fixes, building up a new buffer as an std::string.
|
||||
const char *LastPos = bufferRange.begin();
|
||||
std::string Result;
|
||||
|
||||
for (auto &fix : FixIts) {
|
||||
// We cannot handle overlapping fixits, so assert that they don't happen.
|
||||
assert(LastPos <= fix.getRange().Start.getPointer() &&
|
||||
"Cannot handle overlapping fixits");
|
||||
|
||||
// Keep anything from the last spot we've checked to the start of the fixit.
|
||||
Result.append(LastPos, fix.getRange().Start.getPointer());
|
||||
|
||||
// Replace the content covered by the fixit with the replacement text.
|
||||
Result.append(fix.getText().begin(), fix.getText().end());
|
||||
|
||||
// Next character to consider is at the end of the fixit.
|
||||
LastPos = fix.getRange().End.getPointer();
|
||||
}
|
||||
|
||||
// Retain the end of the file.
|
||||
Result.append(LastPos, bufferRange.end());
|
||||
|
||||
std::error_code error;
|
||||
llvm::raw_fd_ostream outs(memBuffer->getBufferIdentifier(), error,
|
||||
llvm::sys::fs::OpenFlags::F_None);
|
||||
if (!error)
|
||||
outs << Result;
|
||||
}
|
||||
|
||||
/// diagnostics for '<unknown>:0' should be considered as unexpected.
|
||||
static bool
|
||||
verifyUnknown(SourceManager &SM,
|
||||
std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics) {
|
||||
bool HadError = false;
|
||||
for (unsigned i = 0, e = CapturedDiagnostics.size(); i != e; ++i) {
|
||||
if (CapturedDiagnostics[i].Loc.isValid())
|
||||
continue;
|
||||
|
||||
HadError = true;
|
||||
std::string Message =
|
||||
("unexpected " +
|
||||
getDiagKindString(CapturedDiagnostics[i].Classification) +
|
||||
" produced: " + CapturedDiagnostics[i].Message)
|
||||
.str();
|
||||
|
||||
auto diag = SM.GetMessage({}, llvm::SourceMgr::DK_Error, Message, {}, {});
|
||||
SM.getLLVMSourceMgr().PrintMessage(llvm::errs(), diag);
|
||||
}
|
||||
return HadError;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
static unsigned getColumnNumber(StringRef buffer, llvm::SMLoc loc) {
|
||||
assert(loc.getPointer() >= buffer.data());
|
||||
assert((size_t)(loc.getPointer() - buffer.data()) <= buffer.size());
|
||||
@@ -175,17 +221,18 @@ static unsigned getColumnNumber(StringRef buffer, llvm::SMLoc loc) {
|
||||
|
||||
/// Return true if the given \p ExpectedFixIt is in the fix-its emitted by
|
||||
/// diagnostic \p D.
|
||||
static bool checkForFixIt(const ExpectedFixIt &Expected,
|
||||
const llvm::SMDiagnostic &D,
|
||||
StringRef buffer) {
|
||||
for (auto &ActualFixIt : D.getFixIts()) {
|
||||
bool DiagnosticVerifier::checkForFixIt(const ExpectedFixIt &Expected,
|
||||
const CapturedDiagnosticInfo &D,
|
||||
StringRef buffer) {
|
||||
for (auto &ActualFixIt : D.FixIts) {
|
||||
if (ActualFixIt.getText() != Expected.Text)
|
||||
continue;
|
||||
|
||||
llvm::SMRange Range = ActualFixIt.getRange();
|
||||
if (getColumnNumber(buffer, Range.Start) != Expected.StartCol)
|
||||
CharSourceRange Range = ActualFixIt.getRange();
|
||||
if (getColumnNumber(buffer, getRawLoc(Range.getStart())) !=
|
||||
Expected.StartCol)
|
||||
continue;
|
||||
if (getColumnNumber(buffer, Range.End) != Expected.EndCol)
|
||||
if (getColumnNumber(buffer, getRawLoc(Range.getEnd())) != Expected.EndCol)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
@@ -194,16 +241,20 @@ static bool checkForFixIt(const ExpectedFixIt &Expected,
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string renderFixits(ArrayRef<llvm::SMFixIt> fixits,
|
||||
StringRef InputFile) {
|
||||
std::string
|
||||
DiagnosticVerifier::renderFixits(ArrayRef<DiagnosticInfo::FixIt> fixits,
|
||||
StringRef InputFile) {
|
||||
std::string Result;
|
||||
llvm::raw_string_ostream OS(Result);
|
||||
interleave(fixits,
|
||||
[&](const llvm::SMFixIt &ActualFixIt) {
|
||||
llvm::SMRange Range = ActualFixIt.getRange();
|
||||
[&](const DiagnosticInfo::FixIt &ActualFixIt) {
|
||||
CharSourceRange Range = ActualFixIt.getRange();
|
||||
|
||||
OS << "{{" << getColumnNumber(InputFile, Range.Start) << '-'
|
||||
<< getColumnNumber(InputFile, Range.End) << '=';
|
||||
OS << "{{"
|
||||
<< getColumnNumber(InputFile, getRawLoc(Range.getStart()))
|
||||
<< '-'
|
||||
<< getColumnNumber(InputFile, getRawLoc(Range.getEnd()))
|
||||
<< '=';
|
||||
|
||||
for (auto C : ActualFixIt.getText()) {
|
||||
if (C == '\n')
|
||||
@@ -222,8 +273,7 @@ static std::string renderFixits(ArrayRef<llvm::SMFixIt> fixits,
|
||||
/// After the file has been processed, check to see if we got all of
|
||||
/// the expected diagnostics and check to see if there were any unexpected
|
||||
/// ones.
|
||||
DiagnosticVerifier::Result
|
||||
DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
|
||||
using llvm::SMLoc;
|
||||
|
||||
const SourceLoc BufferStartLoc = SM.getLocForBufferStart(BufferID);
|
||||
@@ -256,18 +306,18 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
StringRef MatchStart = InputFile.substr(Match);
|
||||
const char *DiagnosticLoc = MatchStart.data();
|
||||
|
||||
llvm::SourceMgr::DiagKind ExpectedClassification;
|
||||
DiagnosticKind ExpectedClassification;
|
||||
if (MatchStart.startswith("expected-note")) {
|
||||
ExpectedClassification = llvm::SourceMgr::DK_Note;
|
||||
ExpectedClassification = DiagnosticKind::Note;
|
||||
MatchStart = MatchStart.substr(strlen("expected-note"));
|
||||
} else if (MatchStart.startswith("expected-warning")) {
|
||||
ExpectedClassification = llvm::SourceMgr::DK_Warning;
|
||||
ExpectedClassification = DiagnosticKind::Warning;
|
||||
MatchStart = MatchStart.substr(strlen("expected-warning"));
|
||||
} else if (MatchStart.startswith("expected-error")) {
|
||||
ExpectedClassification = llvm::SourceMgr::DK_Error;
|
||||
ExpectedClassification = DiagnosticKind::Error;
|
||||
MatchStart = MatchStart.substr(strlen("expected-error"));
|
||||
} else if (MatchStart.startswith("expected-remark")) {
|
||||
ExpectedClassification = llvm::SourceMgr::DK_Remark;
|
||||
ExpectedClassification = DiagnosticKind::Remark;
|
||||
MatchStart = MatchStart.substr(strlen("expected-remark"));
|
||||
} else
|
||||
continue;
|
||||
@@ -479,13 +529,14 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
|
||||
// Make sure all the expected diagnostics appeared.
|
||||
std::reverse(ExpectedDiagnostics.begin(), ExpectedDiagnostics.end());
|
||||
|
||||
|
||||
for (unsigned i = ExpectedDiagnostics.size(); i != 0; ) {
|
||||
--i;
|
||||
auto &expected = ExpectedDiagnostics[i];
|
||||
|
||||
|
||||
// Check to see if we had this expected diagnostic.
|
||||
auto FoundDiagnosticIter = findDiagnostic(expected, BufferName);
|
||||
auto FoundDiagnosticIter =
|
||||
findDiagnostic(CapturedDiagnostics, expected, BufferName);
|
||||
if (FoundDiagnosticIter == CapturedDiagnostics.end()) {
|
||||
// Diagnostic didn't exist. If this is a 'mayAppear' diagnostic, then
|
||||
// we're ok. Otherwise, leave it in the list.
|
||||
@@ -505,20 +556,20 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
}
|
||||
|
||||
bool matchedAllFixIts =
|
||||
expected.Fixits.size() == FoundDiagnostic.getFixIts().size();
|
||||
|
||||
expected.Fixits.size() == FoundDiagnostic.FixIts.size();
|
||||
|
||||
// If we have any expected fixits that didn't get matched, then they are
|
||||
// wrong. Replace the failed fixit with what actually happened.
|
||||
if (IncorrectFixit) {
|
||||
if (FoundDiagnostic.getFixIts().empty()) {
|
||||
if (FoundDiagnostic.FixIts.empty()) {
|
||||
addError(IncorrectFixit, "expected fix-it not seen");
|
||||
} else {
|
||||
// If we had an incorrect expected fixit, render it and produce a fixit
|
||||
// of our own.
|
||||
auto actual = renderFixits(FoundDiagnostic.getFixIts(), InputFile);
|
||||
auto actual = renderFixits(FoundDiagnostic.FixIts, InputFile);
|
||||
auto replStartLoc = SMLoc::getFromPointer(expected.Fixits[0].StartLoc);
|
||||
auto replEndLoc = SMLoc::getFromPointer(expected.Fixits.back().EndLoc);
|
||||
|
||||
|
||||
llvm::SMFixIt fix(llvm::SMRange(replStartLoc, replEndLoc), actual);
|
||||
addError(IncorrectFixit,
|
||||
"expected fix-it not seen; actual fix-its: " + actual, fix);
|
||||
@@ -528,7 +579,7 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
!expected.mayAppear) {
|
||||
// If there was no fixit specification, but some were produced, add a
|
||||
// fixit to add them in.
|
||||
auto actual = renderFixits(FoundDiagnostic.getFixIts(), InputFile);
|
||||
auto actual = renderFixits(FoundDiagnostic.FixIts, InputFile);
|
||||
auto replStartLoc = SMLoc::getFromPointer(expected.ExpectedEnd - 8); // {{none}} length
|
||||
auto replEndLoc = SMLoc::getFromPointer(expected.ExpectedEnd);
|
||||
|
||||
@@ -551,46 +602,46 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
|
||||
// Check to see if we have any incorrect diagnostics. If so, diagnose them as
|
||||
// such.
|
||||
for (unsigned i = ExpectedDiagnostics.size(); i != 0; ) {
|
||||
--i;
|
||||
auto &expected = ExpectedDiagnostics[i];
|
||||
|
||||
auto expectedDiagIter = ExpectedDiagnostics.begin();
|
||||
while (expectedDiagIter != ExpectedDiagnostics.end()) {
|
||||
// Check to see if any found diagnostics have the right line and
|
||||
// classification, but the wrong text.
|
||||
auto I = CapturedDiagnostics.begin();
|
||||
for (auto E = CapturedDiagnostics.end(); I != E; ++I) {
|
||||
// Verify the file and line of the diagnostic.
|
||||
if (I->getLineNo() != (int)expected.LineNo ||
|
||||
I->getFilename() != BufferName ||
|
||||
I->getKind() != expected.Classification)
|
||||
if (I->Line != expectedDiagIter->LineNo || I->FileName != BufferName ||
|
||||
I->Classification != expectedDiagIter->Classification)
|
||||
continue;
|
||||
|
||||
// Otherwise, we found it, break out.
|
||||
break;
|
||||
}
|
||||
|
||||
if (I == CapturedDiagnostics.end()) continue;
|
||||
if (I == CapturedDiagnostics.end()) {
|
||||
expectedDiagIter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (I->getMessage().find(expected.MessageStr) == StringRef::npos) {
|
||||
auto StartLoc = SMLoc::getFromPointer(expected.MessageRange.begin());
|
||||
auto EndLoc = SMLoc::getFromPointer(expected.MessageRange.end());
|
||||
if (I->Message.find(expectedDiagIter->MessageStr) == StringRef::npos) {
|
||||
auto StartLoc =
|
||||
SMLoc::getFromPointer(expectedDiagIter->MessageRange.begin());
|
||||
auto EndLoc = SMLoc::getFromPointer(expectedDiagIter->MessageRange.end());
|
||||
|
||||
llvm::SMFixIt fixIt(llvm::SMRange{StartLoc, EndLoc}, I->getMessage());
|
||||
addError(expected.MessageRange.begin(), "incorrect message found", fixIt);
|
||||
} else if (I->getColumnNo() + 1 != (int)*expected.ColumnNo) {
|
||||
llvm::SMFixIt fixIt(llvm::SMRange{StartLoc, EndLoc}, I->Message);
|
||||
addError(expectedDiagIter->MessageRange.begin(),
|
||||
"incorrect message found", fixIt);
|
||||
} else if (I->Column != *expectedDiagIter->ColumnNo) {
|
||||
// The difference must be only in the column
|
||||
addError(expected.MessageRange.begin(),
|
||||
addError(expectedDiagIter->MessageRange.begin(),
|
||||
llvm::formatv("message found at column {0} but was expected to "
|
||||
"appear at column {1}",
|
||||
I->getColumnNo() + 1, *expected.ColumnNo));
|
||||
I->Column, *expectedDiagIter->ColumnNo));
|
||||
} else {
|
||||
llvm_unreachable("unhandled difference from expected diagnostic");
|
||||
}
|
||||
CapturedDiagnostics.erase(I);
|
||||
ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i);
|
||||
expectedDiagIter = ExpectedDiagnostics.erase(expectedDiagIter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Diagnose expected diagnostics that didn't appear.
|
||||
std::reverse(ExpectedDiagnostics.begin(), ExpectedDiagnostics.end());
|
||||
@@ -645,19 +696,20 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
|
||||
// Verify that there are no diagnostics (in MemoryBuffer) left in the list.
|
||||
bool HadUnexpectedDiag = false;
|
||||
for (unsigned i = CapturedDiagnostics.size(); i != 0; ) {
|
||||
--i;
|
||||
if (CapturedDiagnostics[i].getFilename() != BufferName) {
|
||||
auto CapturedDiagIter = CapturedDiagnostics.begin();
|
||||
while (CapturedDiagIter != CapturedDiagnostics.end()) {
|
||||
if (CapturedDiagIter->FileName != BufferName) {
|
||||
CapturedDiagIter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
HadUnexpectedDiag = true;
|
||||
std::string Message =
|
||||
"unexpected "+getDiagKindString(CapturedDiagnostics[i].getKind())+
|
||||
" produced: "+CapturedDiagnostics[i].getMessage().str();
|
||||
addError(CapturedDiagnostics[i].getLoc().getPointer(),
|
||||
Message);
|
||||
CapturedDiagnostics.erase(CapturedDiagnostics.begin() + i);
|
||||
("unexpected " + getDiagKindString(CapturedDiagIter->Classification) +
|
||||
" produced: " + CapturedDiagIter->Message)
|
||||
.str();
|
||||
addError(getRawLoc(CapturedDiagIter->Loc).getPointer(), Message);
|
||||
CapturedDiagIter = CapturedDiagnostics.erase(CapturedDiagIter);
|
||||
}
|
||||
|
||||
// Sort the diagnostics by their address in the memory buffer as the primary
|
||||
@@ -672,113 +724,42 @@ DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) {
|
||||
// Emit all of the queue'd up errors.
|
||||
for (auto Err : Errors)
|
||||
SM.getLLVMSourceMgr().PrintMessage(llvm::errs(), Err);
|
||||
|
||||
|
||||
// If auto-apply fixits is on, rewrite the original source file.
|
||||
if (shouldAutoApplyFixes)
|
||||
autoApplyFixes(BufferID, Errors);
|
||||
if (AutoApplyFixes)
|
||||
autoApplyFixes(SM, BufferID, Errors);
|
||||
|
||||
return Result{!Errors.empty(), HadUnexpectedDiag};
|
||||
}
|
||||
|
||||
bool DiagnosticVerifier::verifyUnknown() {
|
||||
bool HadError = false;
|
||||
for (unsigned i = 0, e = CapturedDiagnostics.size(); i != e; ++i) {
|
||||
if (CapturedDiagnostics[i].getFilename() != "<unknown>")
|
||||
continue;
|
||||
|
||||
HadError = true;
|
||||
std::string Message =
|
||||
"unexpected "+getDiagKindString(CapturedDiagnostics[i].getKind())+
|
||||
" produced: "+CapturedDiagnostics[i].getMessage().str();
|
||||
|
||||
auto diag = SM.GetMessage({}, llvm::SourceMgr::DK_Error, Message,
|
||||
{}, {});
|
||||
SM.getLLVMSourceMgr().PrintMessage(llvm::errs(), diag);
|
||||
}
|
||||
return HadError;
|
||||
}
|
||||
|
||||
void DiagnosticVerifier::printRemainingDiagnostics() const {
|
||||
for (const auto &diag : CapturedDiagnostics) {
|
||||
// Determine what kind of diagnostic we're emitting.
|
||||
llvm::SourceMgr::DiagKind SMKind;
|
||||
switch (diag.Classification) {
|
||||
case DiagnosticKind::Error:
|
||||
SMKind = llvm::SourceMgr::DK_Error;
|
||||
break;
|
||||
case DiagnosticKind::Warning:
|
||||
SMKind = llvm::SourceMgr::DK_Warning;
|
||||
break;
|
||||
|
||||
case DiagnosticKind::Note:
|
||||
SMKind = llvm::SourceMgr::DK_Note;
|
||||
break;
|
||||
|
||||
case DiagnosticKind::Remark:
|
||||
SMKind = llvm::SourceMgr::DK_Remark;
|
||||
break;
|
||||
}
|
||||
|
||||
SM.getLLVMSourceMgr().PrintMessage(
|
||||
llvm::errs(), diag.getLoc(), diag.getKind(),
|
||||
"diagnostic produced by Clang: " + diag.getMessage(),
|
||||
/*Ranges=*/ {}, diag.getFixIts());
|
||||
llvm::errs(), getRawLoc(diag.Loc), SMKind,
|
||||
"diagnostic produced by Clang: " + diag.Message.str(),
|
||||
/*Ranges=*/{}, {});
|
||||
}
|
||||
}
|
||||
|
||||
/// If there are any -verify errors (e.g. differences between expectations
|
||||
/// and actual diagnostics produced), apply fixits to the original source
|
||||
/// file and drop it back in place.
|
||||
void DiagnosticVerifier::autoApplyFixes(unsigned BufferID,
|
||||
ArrayRef<llvm::SMDiagnostic> diags) {
|
||||
// Walk the list of diagnostics, pulling out any fixits into an array of just
|
||||
// them.
|
||||
SmallVector<llvm::SMFixIt, 4> FixIts;
|
||||
for (auto &diag : diags)
|
||||
FixIts.append(diag.getFixIts().begin(), diag.getFixIts().end());
|
||||
|
||||
// If we have no fixits to apply, avoid touching the file.
|
||||
if (FixIts.empty())
|
||||
return;
|
||||
|
||||
// Sort the fixits by their start location.
|
||||
std::sort(FixIts.begin(), FixIts.end(),
|
||||
[&](const llvm::SMFixIt &lhs, const llvm::SMFixIt &rhs) -> bool {
|
||||
return lhs.getRange().Start.getPointer()
|
||||
< rhs.getRange().Start.getPointer();
|
||||
});
|
||||
// Coalesce identical fix-its. This happens most often with "expected-error 2"
|
||||
// syntax.
|
||||
FixIts.erase(std::unique(FixIts.begin(), FixIts.end(),
|
||||
[](const llvm::SMFixIt &lhs,
|
||||
const llvm::SMFixIt &rhs) -> bool {
|
||||
return lhs.getRange().Start == rhs.getRange().Start &&
|
||||
lhs.getRange().End == rhs.getRange().End &&
|
||||
lhs.getText() == rhs.getText();
|
||||
}), FixIts.end());
|
||||
// Filter out overlapping fix-its. This allows the compiler to apply changes
|
||||
// to the easy parts of the file, and leave in the tricky cases for the
|
||||
// developer to handle manually.
|
||||
FixIts.erase(swift::removeAdjacentIf(FixIts.begin(), FixIts.end(),
|
||||
[](const llvm::SMFixIt &lhs,
|
||||
const llvm::SMFixIt &rhs) {
|
||||
return lhs.getRange().End.getPointer() > rhs.getRange().Start.getPointer();
|
||||
}), FixIts.end());
|
||||
|
||||
// Get the contents of the original source file.
|
||||
auto memBuffer = SM.getLLVMSourceMgr().getMemoryBuffer(BufferID);
|
||||
auto bufferRange = memBuffer->getBuffer();
|
||||
|
||||
// Apply the fixes, building up a new buffer as an std::string.
|
||||
const char *LastPos = bufferRange.begin();
|
||||
std::string Result;
|
||||
|
||||
for (auto &fix : FixIts) {
|
||||
// We cannot handle overlapping fixits, so assert that they don't happen.
|
||||
assert(LastPos <= fix.getRange().Start.getPointer() &&
|
||||
"Cannot handle overlapping fixits");
|
||||
|
||||
// Keep anything from the last spot we've checked to the start of the fixit.
|
||||
Result.append(LastPos, fix.getRange().Start.getPointer());
|
||||
|
||||
// Replace the content covered by the fixit with the replacement text.
|
||||
Result.append(fix.getText().begin(), fix.getText().end());
|
||||
|
||||
// Next character to consider is at the end of the fixit.
|
||||
LastPos = fix.getRange().End.getPointer();
|
||||
}
|
||||
|
||||
// Retain the end of the file.
|
||||
Result.append(LastPos, bufferRange.end());
|
||||
|
||||
std::error_code error;
|
||||
llvm::raw_fd_ostream outs(memBuffer->getBufferIdentifier(), error,
|
||||
llvm::sys::fs::OpenFlags::F_None);
|
||||
if (!error)
|
||||
outs << Result;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Main entrypoints
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -786,42 +767,48 @@ void DiagnosticVerifier::autoApplyFixes(unsigned BufferID,
|
||||
/// Every time a diagnostic is generated in -verify mode, this function is
|
||||
/// called with the diagnostic. We just buffer them up until the end of the
|
||||
/// file.
|
||||
static void VerifyModeDiagnosticHook(const llvm::SMDiagnostic &Diag,
|
||||
void *Context) {
|
||||
((DiagnosticVerifier*)Context)->addDiagnostic(Diag);
|
||||
void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
|
||||
const DiagnosticInfo &Info) {
|
||||
SmallVector<DiagnosticInfo::FixIt, 2> fixIts;
|
||||
std::copy(Info.FixIts.begin(), Info.FixIts.end(), std::back_inserter(fixIts));
|
||||
|
||||
llvm::SmallString<128> message;
|
||||
{
|
||||
llvm::raw_svector_ostream Out(message);
|
||||
DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString,
|
||||
Info.FormatArgs);
|
||||
}
|
||||
|
||||
if (Info.Loc.isValid()) {
|
||||
const auto lineAndColumn = SM.getLineAndColumn(Info.Loc);
|
||||
const auto fileName = SM.getDisplayNameForLoc(Info.Loc);
|
||||
CapturedDiagnostics.emplace_back(message, fileName, Info.Kind, Info.Loc,
|
||||
lineAndColumn.first, lineAndColumn.second,
|
||||
fixIts);
|
||||
} else {
|
||||
CapturedDiagnostics.emplace_back(message, StringRef(), Info.Kind, Info.Loc,
|
||||
0, 0, fixIts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void swift::enableDiagnosticVerifier(SourceManager &SM) {
|
||||
SM.getLLVMSourceMgr().setDiagHandler(VerifyModeDiagnosticHook,
|
||||
new DiagnosticVerifier(SM));
|
||||
}
|
||||
|
||||
bool swift::verifyDiagnostics(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
|
||||
bool autoApplyFixes, bool ignoreUnknown) {
|
||||
auto *Verifier = (DiagnosticVerifier*)SM.getLLVMSourceMgr().getDiagContext();
|
||||
SM.getLLVMSourceMgr().setDiagHandler(nullptr, nullptr);
|
||||
|
||||
/// Once all diagnostics have been captured, perform verification.
|
||||
bool DiagnosticVerifier::finishProcessing() {
|
||||
DiagnosticVerifier::Result Result = {false, false};
|
||||
|
||||
for (auto &BufferID : BufferIDs) {
|
||||
DiagnosticVerifier::Result FileResult =
|
||||
Verifier->verifyFile(BufferID, autoApplyFixes);
|
||||
DiagnosticVerifier::Result FileResult = verifyFile(BufferID);
|
||||
Result.HadError |= FileResult.HadError;
|
||||
Result.HadUnexpectedDiag |= FileResult.HadUnexpectedDiag;
|
||||
}
|
||||
if (!ignoreUnknown) {
|
||||
bool HadError = Verifier->verifyUnknown();
|
||||
if (!IgnoreUnknown) {
|
||||
bool HadError = verifyUnknown(SM, CapturedDiagnostics);
|
||||
Result.HadError |= HadError;
|
||||
// For <unknown>, all errors are unexpected.
|
||||
Result.HadUnexpectedDiag |= HadError;
|
||||
}
|
||||
|
||||
if (Result.HadUnexpectedDiag)
|
||||
Verifier->printRemainingDiagnostics();
|
||||
|
||||
delete Verifier;
|
||||
printRemainingDiagnostics();
|
||||
|
||||
return Result.HadError;
|
||||
}
|
||||
|
||||
|
||||
@@ -293,6 +293,17 @@ void CompilerInstance::setupStatsReporter() {
|
||||
Stats = std::move(Reporter);
|
||||
}
|
||||
|
||||
void CompilerInstance::setupDiagnosticVerifierIfNeeded() {
|
||||
auto &diagOpts = Invocation.getDiagnosticOptions();
|
||||
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
|
||||
DiagVerifier = std::make_unique<DiagnosticVerifier>(
|
||||
SourceMgr, InputSourceCodeBufferIDs,
|
||||
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
|
||||
diagOpts.VerifyIgnoreUnknown);
|
||||
addDiagnosticConsumer(DiagVerifier.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool CompilerInstance::setup(const CompilerInvocation &Invok) {
|
||||
Invocation = Invok;
|
||||
|
||||
@@ -337,6 +348,7 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) {
|
||||
return true;
|
||||
|
||||
setupStatsReporter();
|
||||
setupDiagnosticVerifierIfNeeded();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -883,6 +883,9 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM,
|
||||
DidErrorOccur = true;
|
||||
}
|
||||
|
||||
if (SuppressOutput)
|
||||
return;
|
||||
|
||||
if (Info.IsChildNote)
|
||||
return;
|
||||
|
||||
|
||||
@@ -2046,15 +2046,22 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
}
|
||||
} FinishDiagProcessingCheckRAII;
|
||||
|
||||
auto finishDiagProcessing = [&](int retValue) -> int {
|
||||
auto finishDiagProcessing = [&](int retValue, bool verifierEnabled) -> int {
|
||||
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
|
||||
bool err = Instance->getDiags().finishProcessing();
|
||||
return retValue ? retValue : err;
|
||||
PDC.setSuppressOutput(false);
|
||||
bool diagnosticsError = Instance->getDiags().finishProcessing();
|
||||
// If the verifier is enabled and did not encounter any verification errors,
|
||||
// return 0 even if the compile failed. This behavior isn't ideal, but large
|
||||
// parts of the test suite are reliant on it.
|
||||
if (verifierEnabled && !diagnosticsError) {
|
||||
return 0;
|
||||
}
|
||||
return retValue ? retValue : diagnosticsError;
|
||||
};
|
||||
|
||||
if (Args.empty()) {
|
||||
Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args);
|
||||
return finishDiagProcessing(1);
|
||||
return finishDiagProcessing(1, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
CompilerInvocation Invocation;
|
||||
@@ -2069,7 +2076,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> configurationFileBuffers;
|
||||
if (Invocation.parseArgs(Args, Instance->getDiags(),
|
||||
&configurationFileBuffers, workingDirectory)) {
|
||||
return finishDiagProcessing(1);
|
||||
return finishDiagProcessing(1, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
// Make an array of PrettyStackTrace objects to dump the configuration files
|
||||
@@ -2111,19 +2118,19 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
Options->PrintHelp(llvm::outs(), displayName(MainExecutablePath).c_str(),
|
||||
"Swift frontend", IncludedFlagsBitmask,
|
||||
ExcludedFlagsBitmask, /*ShowAllAliases*/false);
|
||||
return finishDiagProcessing(0);
|
||||
return finishDiagProcessing(0, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
if (Invocation.getFrontendOptions().PrintTargetInfo) {
|
||||
printTargetInfo(Invocation, llvm::outs());
|
||||
return finishDiagProcessing(0);
|
||||
return finishDiagProcessing(0, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
if (Invocation.getFrontendOptions().RequestedAction ==
|
||||
FrontendOptions::ActionType::NoneAction) {
|
||||
Instance->getDiags().diagnose(SourceLoc(),
|
||||
diag::error_missing_frontend_action);
|
||||
return finishDiagProcessing(1);
|
||||
return finishDiagProcessing(1, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
// Because the serialized diagnostics consumer is initialized here,
|
||||
@@ -2159,9 +2166,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
}
|
||||
|
||||
const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions();
|
||||
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
|
||||
enableDiagnosticVerifier(Instance->getSourceMgr());
|
||||
}
|
||||
bool verifierEnabled = diagOpts.VerifyMode != DiagnosticOptions::NoVerify;
|
||||
|
||||
if (Invocation.getFrontendOptions()
|
||||
.InputsAndOutputs.hasDependencyTrackerPath() ||
|
||||
@@ -2176,7 +2181,7 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
}
|
||||
|
||||
if (Instance->setup(Invocation)) {
|
||||
return finishDiagProcessing(1);
|
||||
return finishDiagProcessing(1, /*verifierEnabled*/ false);
|
||||
}
|
||||
|
||||
// The compiler instance has been configured; notify our observer.
|
||||
@@ -2184,6 +2189,12 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
observer->configuredCompiler(*Instance);
|
||||
}
|
||||
|
||||
if (verifierEnabled) {
|
||||
// Suppress printed diagnostic output during the compile if the verifier is
|
||||
// enabled.
|
||||
PDC.setSuppressOutput(true);
|
||||
}
|
||||
|
||||
int ReturnValue = 0;
|
||||
bool HadError =
|
||||
performCompile(*Instance, Invocation, Args, ReturnValue, observer);
|
||||
@@ -2211,23 +2222,18 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
}
|
||||
}
|
||||
|
||||
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
|
||||
HadError = verifyDiagnostics(
|
||||
Instance->getSourceMgr(),
|
||||
Instance->getInputBufferIDs(),
|
||||
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
|
||||
diagOpts.VerifyIgnoreUnknown);
|
||||
|
||||
if (verifierEnabled) {
|
||||
DiagnosticEngine &diags = Instance->getDiags();
|
||||
if (diags.hasFatalErrorOccurred() &&
|
||||
!Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
|
||||
diags.resetHadAnyError();
|
||||
PDC.setSuppressOutput(false);
|
||||
diags.diagnose(SourceLoc(), diag::verify_encountered_fatal);
|
||||
HadError = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto r = finishDiagProcessing(HadError ? 1 : ReturnValue);
|
||||
auto r = finishDiagProcessing(HadError ? 1 : ReturnValue, verifierEnabled);
|
||||
if (auto *StatsReporter = Instance->getStatsReporter())
|
||||
StatsReporter->noteCurrentProcessExitStatus(r);
|
||||
return r;
|
||||
|
||||
14
test/diagnostics/sil-opt-verifier.sil
Normal file
14
test/diagnostics/sil-opt-verifier.sil
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: not %target-sil-opt -enable-sil-verify-all %s -definite-init -verify 2>&1 | %FileCheck %s
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
// main
|
||||
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
|
||||
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
|
||||
%2 = integer_literal $Builtin.Int32, 0
|
||||
%3 = struct $Int32 (%2 : $Builtin.Int32)
|
||||
return %4 : $Int32 // expected-error {{use of undefined value '%5'}}
|
||||
// CHECK-NOT: error: use of undefined value '%4'
|
||||
// CHECK: incorrect message found
|
||||
}
|
||||
44
test/diagnostics/verifier.swift
Normal file
44
test/diagnostics/verifier.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: not %target-swift-frontend -typecheck -verify -serialize-diagnostics-path %t/serialized.dia -emit-fixits-path %t/fixits %s 2>&1 | %FileCheck %s
|
||||
// RUN: not %target-swift-frontend -typecheck -verify -warnings-as-errors %s 2>&1 | %FileCheck %s -check-prefix CHECK-WARNINGS-AS-ERRORS
|
||||
// RUN: %FileCheck %s -check-prefix CHECK-SERIALIZED <%t/serialized.dia
|
||||
// RUN: %FileCheck %s -check-prefix CHECK-FIXITS <%t/fixits
|
||||
|
||||
// Wrong message
|
||||
let x: Int = "hello, world!" // expected-error {{foo bar baz}}
|
||||
// CHECK-NOT: error: cannot convert value of type 'String' to specified type 'Int'
|
||||
// CHECK: error: incorrect message found
|
||||
|
||||
// Wrong column
|
||||
let y: Int = "hello, world!" // expected-error@:49 {{cannot convert value of type}}
|
||||
// CHECK: message found at column 14 but was expected to appear at column 49
|
||||
|
||||
// Wrong fix-it
|
||||
let z: Int = "hello, world!" as Any
|
||||
// expected-error@-1 {{cannot convert value of type}} {{3-3=foobarbaz}}
|
||||
// CHECK: expected fix-it not seen; actual fix-its: {{[{][{]}}36-36= as! Int{{[}][}]}}
|
||||
|
||||
// Expected no fix-it
|
||||
let a: Bool = "hello, world!" as Any
|
||||
// expected-error@-1 {{cannot convert value of type}} {{none}}
|
||||
// CHECK: expected no fix-its; actual fix-it seen: {{[{][{]}}37-37= as! Bool{{[}][}]}}
|
||||
|
||||
// Unexpected error
|
||||
_ = foo()
|
||||
// CHECK: unexpected error produced: use of unresolved identifier 'foo'
|
||||
|
||||
func b() {
|
||||
let c = 2
|
||||
}
|
||||
// CHECK: unexpected warning produced: initialization of immutable value 'c' was never used
|
||||
// CHECK-WARNINGS-AS-ERRORS: unexpected error produced: initialization of immutable value 'c' was never used
|
||||
|
||||
// Verify the serialized diags have the right magic at the top.
|
||||
// CHECK-SERIALIZED: DIA
|
||||
|
||||
// Ensure the verifier doesn't interfere with -emit-fixits-path.
|
||||
// CHECK-FIXITS: {
|
||||
// CHECK-FIXITS: "file":
|
||||
// CHECK-FIXITS: "offset":
|
||||
// CHECK-FIXITS: "text": " as! Int",
|
||||
// CHECK-FIXITS: },
|
||||
@@ -349,6 +349,9 @@ int main(int argc, char **argv) {
|
||||
Invocation.getLangOptions().EnableExperimentalDifferentiableProgramming =
|
||||
EnableExperimentalDifferentiableProgramming;
|
||||
|
||||
Invocation.getDiagnosticOptions().VerifyMode =
|
||||
VerifyMode ? DiagnosticOptions::Verify : DiagnosticOptions::NoVerify;
|
||||
|
||||
// Setup the SIL Options.
|
||||
SILOptions &SILOpts = Invocation.getSILOptions();
|
||||
SILOpts.InlineThreshold = SILInlineThreshold;
|
||||
@@ -404,15 +407,40 @@ int main(int argc, char **argv) {
|
||||
PrintingDiagnosticConsumer PrintDiags;
|
||||
CI.addDiagnosticConsumer(&PrintDiags);
|
||||
|
||||
if (VerifyMode)
|
||||
PrintDiags.setSuppressOutput(true);
|
||||
|
||||
struct FinishDiagProcessingCheckRAII {
|
||||
bool CalledFinishDiagProcessing = false;
|
||||
~FinishDiagProcessingCheckRAII() {
|
||||
assert(CalledFinishDiagProcessing &&
|
||||
"returned from the function "
|
||||
"without calling finishDiagProcessing");
|
||||
}
|
||||
} FinishDiagProcessingCheckRAII;
|
||||
|
||||
auto finishDiagProcessing = [&](int retValue) -> int {
|
||||
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
|
||||
PrintDiags.setSuppressOutput(false);
|
||||
bool diagnosticsError = CI.getDiags().finishProcessing();
|
||||
// If the verifier is enabled and did not encounter any verification errors,
|
||||
// return 0 even if the compile failed. This behavior isn't ideal, but large
|
||||
// parts of the test suite are reliant on it.
|
||||
if (VerifyMode && !diagnosticsError) {
|
||||
return 0;
|
||||
}
|
||||
return retValue ? retValue : diagnosticsError;
|
||||
};
|
||||
|
||||
if (CI.setup(Invocation))
|
||||
return 1;
|
||||
return finishDiagProcessing(1);
|
||||
|
||||
CI.performSema();
|
||||
|
||||
// If parsing produced an error, don't run any passes.
|
||||
bool HadError = CI.getASTContext().hadError();
|
||||
if (HadError)
|
||||
return 1;
|
||||
return finishDiagProcessing(1);
|
||||
|
||||
// Load the SIL if we have a module. We have to do this after SILParse
|
||||
// creating the unfortunate double if statement.
|
||||
@@ -429,11 +457,6 @@ int main(int argc, char **argv) {
|
||||
SL->getAll();
|
||||
}
|
||||
|
||||
// If we're in verify mode, install a custom diagnostic handling for
|
||||
// SourceMgr.
|
||||
if (VerifyMode)
|
||||
enableDiagnosticVerifier(CI.getSourceMgr());
|
||||
|
||||
if (CI.getSILModule())
|
||||
CI.getSILModule()->setSerializeSILAction([]{});
|
||||
|
||||
@@ -511,7 +534,7 @@ int main(int argc, char **argv) {
|
||||
if (EC) {
|
||||
llvm::errs() << "while opening '" << OutputFile << "': "
|
||||
<< EC.message() << '\n';
|
||||
return 1;
|
||||
return finishDiagProcessing(1);
|
||||
}
|
||||
CI.getSILModule()->print(OS, CI.getMainModule(), SILOpts,
|
||||
!DisableASTDump);
|
||||
@@ -520,12 +543,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
HadError |= CI.getASTContext().hadError();
|
||||
|
||||
// If we're in -verify mode, we've buffered up all of the generated
|
||||
// diagnostics. Check now to ensure that they meet our expectations.
|
||||
if (VerifyMode) {
|
||||
HadError = verifyDiagnostics(CI.getSourceMgr(), CI.getInputBufferIDs(),
|
||||
/*autoApplyFixes*/false,
|
||||
/*ignoreUnknown*/false);
|
||||
DiagnosticEngine &diags = CI.getDiags();
|
||||
if (diags.hasFatalErrorOccurred() &&
|
||||
!Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
|
||||
@@ -535,5 +553,5 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
return HadError;
|
||||
return finishDiagProcessing(HadError);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user