mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Diagnostics] Remove the C++ formatter for the "Swift" diagnostic style
We'll be using the new swift-syntax diagnostic formatter in the near future, as it is nearly available on all host platforms. So, remove the C++ formatter that did source-line annotation, falling back to the "LLVM" style when swift-syntax is not compiled in.
This commit is contained in:
@@ -410,8 +410,6 @@ public:
|
|||||||
SourceLoc();
|
SourceLoc();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getLineString(unsigned BufferID, unsigned LineNumber);
|
|
||||||
|
|
||||||
/// Retrieve the buffer ID for \p Path, loading if necessary.
|
/// Retrieve the buffer ID for \p Path, loading if necessary.
|
||||||
unsigned getExternalSourceBufferID(StringRef Path);
|
unsigned getExternalSourceBufferID(StringRef Path);
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "llvm/Support/Process.h"
|
#include "llvm/Support/Process.h"
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
class AnnotatedSourceSnippet;
|
|
||||||
|
|
||||||
/// Diagnostic consumer that displays diagnostics to standard error.
|
/// Diagnostic consumer that displays diagnostics to standard error.
|
||||||
class PrintingDiagnosticConsumer : public DiagnosticConsumer {
|
class PrintingDiagnosticConsumer : public DiagnosticConsumer {
|
||||||
@@ -38,10 +37,6 @@ class PrintingDiagnosticConsumer : public DiagnosticConsumer {
|
|||||||
bool DidErrorOccur = false;
|
bool DidErrorOccur = false;
|
||||||
DiagnosticOptions::FormattingStyle FormattingStyle =
|
DiagnosticOptions::FormattingStyle FormattingStyle =
|
||||||
DiagnosticOptions::FormattingStyle::LLVM;
|
DiagnosticOptions::FormattingStyle::LLVM;
|
||||||
// The current snippet used to display an error/warning/remark and the notes
|
|
||||||
// implicitly associated with it. Uses `std::unique_ptr` so that
|
|
||||||
// `AnnotatedSourceSnippet` can be forward declared.
|
|
||||||
std::unique_ptr<AnnotatedSourceSnippet> currentSnippet;
|
|
||||||
// Educational notes which are buffered until the consumer is finished
|
// Educational notes which are buffered until the consumer is finished
|
||||||
// constructing a snippet.
|
// constructing a snippet.
|
||||||
SmallVector<std::string, 1> BufferedEducationalNotes;
|
SmallVector<std::string, 1> BufferedEducationalNotes;
|
||||||
|
|||||||
@@ -313,658 +313,8 @@ namespace {
|
|||||||
printer.visit(document);
|
printer.visit(document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Experimental diagnostic printing.
|
|
||||||
|
|
||||||
static void printDiagnosticKind(DiagnosticKind kind, raw_ostream &out) {
|
|
||||||
switch (kind) {
|
|
||||||
case DiagnosticKind::Error:
|
|
||||||
out.changeColor(ColoredStream::Colors::RED, true);
|
|
||||||
out << "error:";
|
|
||||||
break;
|
|
||||||
case DiagnosticKind::Warning:
|
|
||||||
out.changeColor(ColoredStream::Colors::YELLOW, true);
|
|
||||||
out << "warning:";
|
|
||||||
break;
|
|
||||||
case DiagnosticKind::Note:
|
|
||||||
out.changeColor(ColoredStream::Colors::CYAN, true);
|
|
||||||
out << "note:";
|
|
||||||
break;
|
|
||||||
case DiagnosticKind::Remark:
|
|
||||||
out.changeColor(ColoredStream::Colors::CYAN, true);
|
|
||||||
out << "remark:";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out.resetColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printNumberedGutter(unsigned LineNumber,
|
|
||||||
unsigned LineNumberIndent, raw_ostream &Out) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << llvm::formatv(
|
|
||||||
"{0} | ",
|
|
||||||
llvm::fmt_align(LineNumber, llvm::AlignStyle::Right, LineNumberIndent));
|
|
||||||
Out.resetColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printEmptyGutter(unsigned LineNumberIndent, raw_ostream &Out) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << std::string(LineNumberIndent + 1, ' ') << "| ";
|
|
||||||
Out.resetColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printStringAsSingleQuotedLine(StringRef str, raw_ostream &Out) {
|
|
||||||
Out << "'";
|
|
||||||
for (auto character : str) {
|
|
||||||
if (character == '\n')
|
|
||||||
Out << "\\n";
|
|
||||||
else
|
|
||||||
Out << character;
|
|
||||||
}
|
|
||||||
Out << "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe a fix-it out-of-line.
|
|
||||||
static void describeFixIt(SourceManager &SM, DiagnosticInfo::FixIt fixIt,
|
|
||||||
raw_ostream &Out) {
|
|
||||||
if (fixIt.getRange().getByteLength() == 0) {
|
|
||||||
Out << "insert ";
|
|
||||||
printStringAsSingleQuotedLine(fixIt.getText(), Out);
|
|
||||||
} else if (fixIt.getText().empty()) {
|
|
||||||
Out << "remove ";
|
|
||||||
printStringAsSingleQuotedLine(SM.extractText(fixIt.getRange()), Out);
|
|
||||||
} else {
|
|
||||||
Out << "replace ";
|
|
||||||
printStringAsSingleQuotedLine(SM.extractText(fixIt.getRange()), Out);
|
|
||||||
Out << " with ";
|
|
||||||
printStringAsSingleQuotedLine(fixIt.getText(), Out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void describeFixIts(SourceManager &SM,
|
|
||||||
ArrayRef<DiagnosticInfo::FixIt> fixIts,
|
|
||||||
raw_ostream &Out) {
|
|
||||||
Out << "[";
|
|
||||||
for (unsigned i = 0; i < fixIts.size(); ++i) {
|
|
||||||
if (fixIts.size() > 2 && i + 1 == fixIts.size()) {
|
|
||||||
Out << ", and ";
|
|
||||||
} else if (fixIts.size() > 2 && i > 0) {
|
|
||||||
Out << ", ";
|
|
||||||
} else if (fixIts.size() == 2 && i == 1) {
|
|
||||||
Out << " and ";
|
|
||||||
}
|
|
||||||
describeFixIt(SM, fixIts[i], Out);
|
|
||||||
}
|
|
||||||
Out << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a single line of source code annotated with optional messages,
|
|
||||||
/// highlights, and fix-its.
|
|
||||||
class AnnotatedLine {
|
|
||||||
friend class AnnotatedFileExcerpt;
|
|
||||||
|
|
||||||
// A diagnostic message located at a specific byte in the line.
|
|
||||||
struct Message {
|
|
||||||
unsigned Byte;
|
|
||||||
DiagnosticKind Kind;
|
|
||||||
std::string Text;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A half-open byte range which should be highlighted.
|
|
||||||
struct Highlight {
|
|
||||||
unsigned StartByte;
|
|
||||||
unsigned EndByte;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A half-open byte range which should be replaced with the given text.
|
|
||||||
struct FixIt {
|
|
||||||
unsigned StartByte;
|
|
||||||
unsigned EndByte;
|
|
||||||
std::string Text;
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned LineNumber;
|
|
||||||
// The line number displayed to the user. This may differ from the actual
|
|
||||||
// line number if #sourceLocation is used.
|
|
||||||
unsigned DisplayLineNumber;
|
|
||||||
std::string LineText;
|
|
||||||
SmallVector<Message, 1> Messages;
|
|
||||||
SmallVector<Highlight, 1> Highlights;
|
|
||||||
SmallVector<FixIt, 1> FixIts;
|
|
||||||
|
|
||||||
// Adjust output color as needed if this byte is part of a fix-it deletion.
|
|
||||||
void applyStyleForLineByte(unsigned Byte, raw_ostream &Out, bool &Deleted) {
|
|
||||||
bool shouldDelete = false;
|
|
||||||
|
|
||||||
for (auto fixIt : FixIts) {
|
|
||||||
if (Byte >= fixIt.StartByte && Byte < fixIt.EndByte)
|
|
||||||
shouldDelete = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only modify deletions when we reach the start or end of
|
|
||||||
// a fix-it. This ensures that so long as the original
|
|
||||||
// SourceLocs pointed to the first byte of a grapheme cluster, we won't
|
|
||||||
// output an ANSI escape sequence in the middle of one.
|
|
||||||
if (shouldDelete != Deleted) {
|
|
||||||
Out.resetColor();
|
|
||||||
if (shouldDelete) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::RED, /*bold*/ true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Deleted = shouldDelete;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert fix-it replacement text at the appropriate point in the line.
|
|
||||||
bool maybePrintInsertionAfter(int Byte, bool isLineASCII,
|
|
||||||
raw_ostream &Out) {
|
|
||||||
// Don't print insertions inline for non-ASCII lines, because we can't
|
|
||||||
// print an underline beneath them.
|
|
||||||
if (!isLineASCII)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (auto fixIt : FixIts) {
|
|
||||||
if ((int)fixIt.EndByte - 1 == Byte) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::GREEN, /*bold*/ true);
|
|
||||||
for (unsigned i = 0; i < fixIt.Text.size(); ++i) {
|
|
||||||
// Invert text colors for editor placeholders.
|
|
||||||
if (i + 1 < fixIt.Text.size() && fixIt.Text.substr(i, 2) == "<#") {
|
|
||||||
Out.changeColor(ColoredStream::Colors::GREEN, /*bold*/ true,
|
|
||||||
/*background*/ true);
|
|
||||||
++i;
|
|
||||||
} else if (i + 1 < fixIt.Text.size() &&
|
|
||||||
fixIt.Text.substr(i, 2) == "#>") {
|
|
||||||
Out.changeColor(ColoredStream::Colors::GREEN, /*bold*/ true,
|
|
||||||
/*background*/ false);
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
Out << fixIt.Text[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Out.resetColor();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned lineByteOffsetForLoc(SourceManager &SM, SourceLoc Loc) {
|
|
||||||
SourceLoc lineStart = SM.getLocForLineCol(SM.findBufferContainingLoc(Loc),
|
|
||||||
getLineNumber(), 1);
|
|
||||||
return SM.getByteDistance(lineStart, Loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
AnnotatedLine(unsigned LineNumber, unsigned DisplayLineNumber,
|
|
||||||
StringRef LineText)
|
|
||||||
: LineNumber(LineNumber), DisplayLineNumber(DisplayLineNumber),
|
|
||||||
LineText(LineText) {}
|
|
||||||
|
|
||||||
unsigned getLineNumber() { return LineNumber; }
|
|
||||||
|
|
||||||
void addMessage(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
|
|
||||||
StringRef Message) {
|
|
||||||
Messages.push_back({lineByteOffsetForLoc(SM, Loc), Kind, Message.str()});
|
|
||||||
}
|
|
||||||
|
|
||||||
void addHighlight(SourceManager &SM, CharSourceRange Range) {
|
|
||||||
Highlights.push_back({lineByteOffsetForLoc(SM, Range.getStart()),
|
|
||||||
lineByteOffsetForLoc(SM, Range.getEnd())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFixIt(SourceManager &SM, CharSourceRange Range, StringRef Text) {
|
|
||||||
FixIts.push_back({lineByteOffsetForLoc(SM, Range.getStart()),
|
|
||||||
lineByteOffsetForLoc(SM, Range.getEnd()), Text.str()});
|
|
||||||
}
|
|
||||||
|
|
||||||
void render(unsigned LineNumberIndent, raw_ostream &Out) {
|
|
||||||
printNumberedGutter(DisplayLineNumber, LineNumberIndent, Out);
|
|
||||||
|
|
||||||
// Determine if the line is all-ASCII. This will determine a number of
|
|
||||||
// later formatting decisions.
|
|
||||||
bool isASCII = true;
|
|
||||||
for (unsigned i = 0; i < LineText.size(); ++i)
|
|
||||||
isASCII = isASCII && static_cast<unsigned char>(LineText[i]) <= 127;
|
|
||||||
|
|
||||||
// Map a byte in the original source line to a column in the annotated
|
|
||||||
// line.
|
|
||||||
unsigned *byteToColumnMap = new unsigned[LineText.size() + 1];
|
|
||||||
unsigned extraColumns = 0;
|
|
||||||
// Track the location of the first character in the line that is not a
|
|
||||||
// whitespace character. This can be used to avoid underlining leading
|
|
||||||
// whitespace, which looks weird even though it is technically accurate.
|
|
||||||
unsigned firstNonWhitespaceColumn = 0;
|
|
||||||
bool seenNonWhitespaceCharacter = false;
|
|
||||||
// We count one past the end of LineText here to handle trailing fix-it
|
|
||||||
// insertions.
|
|
||||||
for (unsigned i = 0; i < LineText.size() + 1; ++i) {
|
|
||||||
if (isASCII) {
|
|
||||||
for (auto fixIt : FixIts) {
|
|
||||||
if (fixIt.EndByte == i) {
|
|
||||||
// We don't print editor placeholder indicators, so make sure we
|
|
||||||
// don't count them here.
|
|
||||||
extraColumns += fixIt.Text.size() -
|
|
||||||
StringRef(fixIt.Text).count("<#") * 2 -
|
|
||||||
StringRef(fixIt.Text).count("#>") * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < LineText.size()) {
|
|
||||||
// Tabs are mapped to 2 spaces so they have a known column width.
|
|
||||||
if (LineText[i] == '\t')
|
|
||||||
extraColumns += 1;
|
|
||||||
|
|
||||||
if (!seenNonWhitespaceCharacter && !isspace(LineText[i])) {
|
|
||||||
firstNonWhitespaceColumn = i + extraColumns;
|
|
||||||
seenNonWhitespaceCharacter = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byteToColumnMap[i] = i + extraColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the source line byte-by-byte, emitting ANSI escape sequences as
|
|
||||||
// needed to style fix-its, and checking for non-ASCII characters.
|
|
||||||
bool deleted = false;
|
|
||||||
maybePrintInsertionAfter(-1, isASCII, Out);
|
|
||||||
for (unsigned i = 0; i < LineText.size(); ++i) {
|
|
||||||
applyStyleForLineByte(i, Out, deleted);
|
|
||||||
if (LineText[i] == '\t')
|
|
||||||
Out << " ";
|
|
||||||
else
|
|
||||||
Out << LineText[i];
|
|
||||||
if (maybePrintInsertionAfter(i, isASCII, Out)) {
|
|
||||||
deleted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maybePrintInsertionAfter(LineText.size(), isASCII, Out);
|
|
||||||
Out.resetColor();
|
|
||||||
Out << "\n";
|
|
||||||
|
|
||||||
// If the entire line is composed of ASCII characters, we can position '~'
|
|
||||||
// characters in the appropriate columns on the following line to
|
|
||||||
// represent highlights.
|
|
||||||
if (isASCII) {
|
|
||||||
auto highlightLine = std::string(byteToColumnMap[LineText.size()], ' ');
|
|
||||||
for (auto highlight : Highlights) {
|
|
||||||
for (unsigned i =
|
|
||||||
std::max(highlight.StartByte, firstNonWhitespaceColumn);
|
|
||||||
i < highlight.EndByte; ++i)
|
|
||||||
highlightLine[byteToColumnMap[i]] = '~';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto fixIt : FixIts) {
|
|
||||||
// Mark deletions.
|
|
||||||
for (unsigned i = std::max(fixIt.StartByte, firstNonWhitespaceColumn);
|
|
||||||
i < fixIt.EndByte; ++i)
|
|
||||||
highlightLine[byteToColumnMap[i]] = '-';
|
|
||||||
// Mark insertions. If the fix-it starts at the beginning of the line,
|
|
||||||
// highlight from column zero to the end column. Otherwise, find the
|
|
||||||
// column which immediately precedes the insertion. Then, highlight
|
|
||||||
// from the column after that to the end column. The end column in
|
|
||||||
// this case is obtained from the fix-it's starting byte, because
|
|
||||||
// insertions are printed before the deleted range.
|
|
||||||
unsigned startColumn = fixIt.StartByte == 0
|
|
||||||
? 0
|
|
||||||
: byteToColumnMap[fixIt.StartByte - 1] + 1;
|
|
||||||
for (unsigned i = startColumn; i < byteToColumnMap[fixIt.StartByte];
|
|
||||||
++i)
|
|
||||||
highlightLine[i] = '+';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the highlight line with the appropriate colors.
|
|
||||||
if (!(Highlights.empty() && FixIts.empty())) {
|
|
||||||
printEmptyGutter(LineNumberIndent, Out);
|
|
||||||
auto currentColor = ColoredStream::Colors::WHITE;
|
|
||||||
for (unsigned i = 0; i < highlightLine.size(); ++i) {
|
|
||||||
llvm::raw_ostream::Colors charColor;
|
|
||||||
switch (highlightLine[i]) {
|
|
||||||
case '+':
|
|
||||||
charColor = ColoredStream::Colors::GREEN;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
charColor = ColoredStream::Colors::RED;
|
|
||||||
break;
|
|
||||||
case '~':
|
|
||||||
charColor = ColoredStream::Colors::BLUE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
charColor = ColoredStream::Colors::WHITE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (currentColor != charColor) {
|
|
||||||
currentColor = charColor;
|
|
||||||
Out.changeColor(charColor, /*bold*/ true);
|
|
||||||
}
|
|
||||||
Out << highlightLine[i];
|
|
||||||
}
|
|
||||||
Out.resetColor();
|
|
||||||
Out << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print each message on its own line below the source line. If the source
|
|
||||||
// line is ASCII, we can insert a caret pointing directly to the message
|
|
||||||
// location. If not, use a more generic "-->" indicator.
|
|
||||||
// FIXME: Improve Unicode support so every message can include a direct
|
|
||||||
// location indicator.
|
|
||||||
for (auto msg : Messages) {
|
|
||||||
printEmptyGutter(LineNumberIndent, Out);
|
|
||||||
if (isASCII) {
|
|
||||||
Out << std::string(byteToColumnMap[msg.Byte], ' ') << "^ ";
|
|
||||||
} else {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << "--> ";
|
|
||||||
Out.resetColor();
|
|
||||||
}
|
|
||||||
printDiagnosticKind(msg.Kind, Out);
|
|
||||||
Out.resetColor();
|
|
||||||
Out.changeColor(ColoredStream::Colors::WHITE, /*bold*/ true);
|
|
||||||
Out << " " << msg.Text << "\n";
|
|
||||||
Out.resetColor();
|
|
||||||
}
|
|
||||||
delete[] byteToColumnMap;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents an excerpt of a source file which contains one or more
|
|
||||||
/// annotated source lines.
|
|
||||||
class AnnotatedFileExcerpt {
|
|
||||||
SourceManager &SM;
|
|
||||||
unsigned BufferID;
|
|
||||||
/// The primary location of the parent error/warning/remark for this
|
|
||||||
/// diagnostic message. This is printed alongside the file path so it can be
|
|
||||||
/// parsed by editors and other tooling.
|
|
||||||
SourceLoc PrimaryLoc;
|
|
||||||
/// Whether the excerpt is from a virtual file (e.g. one introduced using
|
|
||||||
/// #sourceLocation).
|
|
||||||
bool FromVirtualFile;
|
|
||||||
std::vector<AnnotatedLine> AnnotatedLines;
|
|
||||||
|
|
||||||
/// Return the AnnotatedLine for a given SourceLoc, creating it if it
|
|
||||||
/// doesn't already exist.
|
|
||||||
AnnotatedLine &lineForLoc(SourceLoc Loc) {
|
|
||||||
// FIXME: This call to `getLineNumber` is expensive.
|
|
||||||
unsigned lineNo = SM.getLineAndColumnInBuffer(Loc).first;
|
|
||||||
AnnotatedLine newLine(lineNo, 0, "");
|
|
||||||
auto iter =
|
|
||||||
std::lower_bound(AnnotatedLines.begin(), AnnotatedLines.end(),
|
|
||||||
newLine, [](AnnotatedLine l1, AnnotatedLine l2) {
|
|
||||||
return l1.getLineNumber() < l2.getLineNumber();
|
|
||||||
});
|
|
||||||
if (iter == AnnotatedLines.end() || iter->getLineNumber() != lineNo) {
|
|
||||||
newLine.LineText = SM.getLineString(BufferID, lineNo);
|
|
||||||
newLine.DisplayLineNumber =
|
|
||||||
SM.getPresumedLineAndColumnForLoc(Loc).first;
|
|
||||||
return *AnnotatedLines.insert(iter, newLine);
|
|
||||||
} else {
|
|
||||||
return *iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printNumberedLine(SourceManager &SM, unsigned BufferID,
|
|
||||||
unsigned LineNumber, unsigned LineNumberIndent,
|
|
||||||
raw_ostream &Out) {
|
|
||||||
printNumberedGutter(LineNumber, LineNumberIndent, Out);
|
|
||||||
Out << SM.getLineString(BufferID, LineNumber) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void lineRangesForRange(CharSourceRange Range,
|
|
||||||
SmallVectorImpl<CharSourceRange> &LineRanges) {
|
|
||||||
unsigned startLineNo =
|
|
||||||
SM.getLineAndColumnInBuffer(Range.getStart()).first;
|
|
||||||
unsigned endLineNo = SM.getLineAndColumnInBuffer(Range.getEnd()).first;
|
|
||||||
|
|
||||||
if (startLineNo == endLineNo) {
|
|
||||||
LineRanges.push_back(Range);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split the range by line.
|
|
||||||
SourceLoc lineEnd = SM.getLocForOffset(
|
|
||||||
BufferID, *SM.resolveOffsetForEndOfLine(BufferID, startLineNo));
|
|
||||||
LineRanges.push_back(CharSourceRange(SM, Range.getStart(), lineEnd));
|
|
||||||
|
|
||||||
for (unsigned intermediateLine = startLineNo + 1;
|
|
||||||
intermediateLine < endLineNo; ++intermediateLine) {
|
|
||||||
SourceLoc lineStart =
|
|
||||||
SM.getLocForLineCol(BufferID, intermediateLine, 1);
|
|
||||||
SourceLoc lineEnd = SM.getLocForOffset(
|
|
||||||
BufferID,
|
|
||||||
*SM.resolveOffsetForEndOfLine(BufferID, intermediateLine));
|
|
||||||
LineRanges.push_back(CharSourceRange(SM, lineStart, lineEnd));
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLoc lastLineStart = SM.getLocForLineCol(BufferID, endLineNo, 1);
|
|
||||||
LineRanges.push_back(CharSourceRange(SM, lastLineStart, Range.getEnd()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void printLineEllipsis(raw_ostream &Out) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN, true);
|
|
||||||
Out << llvm::formatv("{0}...\n",
|
|
||||||
llvm::fmt_repeat(" ", getPreferredLineNumberIndent()));
|
|
||||||
Out.resetColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
AnnotatedFileExcerpt(SourceManager &SM, unsigned BufferID,
|
|
||||||
SourceLoc PrimaryLoc)
|
|
||||||
: SM(SM), BufferID(BufferID), PrimaryLoc(PrimaryLoc) {
|
|
||||||
FromVirtualFile = SM.isLocInVirtualFile(PrimaryLoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getPreferredLineNumberIndent() {
|
|
||||||
// The lines are already in sorted ascending order, and we render one line
|
|
||||||
// after the last one for context. Use the last line number plus one to
|
|
||||||
// determine the indent.
|
|
||||||
return floor(1 + log10(AnnotatedLines.back().getLineNumber() + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMessage(SourceLoc Loc, DiagnosticKind Kind, StringRef Message) {
|
|
||||||
lineForLoc(Loc).addMessage(SM, Loc, Kind, Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addHighlight(CharSourceRange Range) {
|
|
||||||
SmallVector<CharSourceRange, 1> ranges;
|
|
||||||
lineRangesForRange(Range, ranges);
|
|
||||||
for (auto lineRange : ranges)
|
|
||||||
lineForLoc(lineRange.getStart()).addHighlight(SM, lineRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFixIt(CharSourceRange Range, StringRef Text) {
|
|
||||||
SmallVector<CharSourceRange, 1> ranges;
|
|
||||||
lineRangesForRange(Range, ranges);
|
|
||||||
// The removals are broken down line-by-line, so only add any insertions
|
|
||||||
// to the last replacement.
|
|
||||||
auto last = ranges.pop_back_val();
|
|
||||||
lineForLoc(last.getStart()).addFixIt(SM, last, Text);
|
|
||||||
for (auto lineRange : ranges)
|
|
||||||
lineForLoc(lineRange.getStart()).addFixIt(SM, lineRange, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void render(unsigned MinimumLineNumberIndent, raw_ostream &Out) {
|
|
||||||
// Tha maximum number of intermediate lines without annotations to render
|
|
||||||
// between annotated lines before using an ellipsis.
|
|
||||||
static const unsigned maxIntermediateLines = 3;
|
|
||||||
|
|
||||||
assert(!AnnotatedLines.empty() && "File excerpt has no lines");
|
|
||||||
unsigned lineNumberIndent =
|
|
||||||
std::max(getPreferredLineNumberIndent(), MinimumLineNumberIndent);
|
|
||||||
|
|
||||||
// Print the file name at the top of each excerpt.
|
|
||||||
auto primaryLineAndColumn = SM.getPresumedLineAndColumnForLoc(PrimaryLoc);
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << std::string(lineNumberIndent + 1, '=') << " "
|
|
||||||
<< SM.getDisplayNameForLoc(PrimaryLoc) << ":"
|
|
||||||
<< primaryLineAndColumn.first << ":" << primaryLineAndColumn.second
|
|
||||||
<< " " << std::string(lineNumberIndent + 1, '=') << "\n";
|
|
||||||
Out.resetColor();
|
|
||||||
|
|
||||||
// Print one extra line at the top for context, so long as this isn't an
|
|
||||||
// excerpt from a virtual file.
|
|
||||||
if (AnnotatedLines.front().getLineNumber() > 1 && !FromVirtualFile)
|
|
||||||
printNumberedLine(SM, BufferID,
|
|
||||||
AnnotatedLines.front().getLineNumber() - 1,
|
|
||||||
lineNumberIndent, Out);
|
|
||||||
|
|
||||||
// Render the first annotated line.
|
|
||||||
AnnotatedLines.front().render(lineNumberIndent, Out);
|
|
||||||
unsigned lastLineNumber = AnnotatedLines.front().getLineNumber();
|
|
||||||
|
|
||||||
// Render intermediate lines/ellipsis, followed by the next annotated
|
|
||||||
// line until they have all been output.
|
|
||||||
for (auto line = AnnotatedLines.begin() + 1; line != AnnotatedLines.end();
|
|
||||||
++line) {
|
|
||||||
unsigned lineNumber = line->getLineNumber();
|
|
||||||
if (FromVirtualFile) {
|
|
||||||
// Don't print intermediate lines in virtual files, as they may not
|
|
||||||
// make sense in context. Instead, just print an ellipsis between them
|
|
||||||
// if they're not consecutive in the actual source file.
|
|
||||||
if (lineNumber - lastLineNumber > 1) {
|
|
||||||
printLineEllipsis(Out);
|
|
||||||
}
|
|
||||||
} else if (lineNumber - lastLineNumber > maxIntermediateLines) {
|
|
||||||
// Use an ellipsis to denote an omitted part of the file.
|
|
||||||
printNumberedLine(SM, BufferID, lastLineNumber + 1, lineNumberIndent,
|
|
||||||
Out);
|
|
||||||
printLineEllipsis(Out);
|
|
||||||
printNumberedLine(SM, BufferID, lineNumber - 1, lineNumberIndent,
|
|
||||||
Out);
|
|
||||||
} else {
|
|
||||||
// Print all the intermediate lines.
|
|
||||||
for (unsigned l = lastLineNumber + 1; l < lineNumber; ++l) {
|
|
||||||
printNumberedLine(SM, BufferID, l, lineNumberIndent, Out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Print the annotated line.
|
|
||||||
line->render(lineNumberIndent, Out);
|
|
||||||
lastLineNumber = lineNumber;
|
|
||||||
}
|
|
||||||
// Print one extra line at the bottom for context, so long as the excerpt
|
|
||||||
// isn't from a virtual file.
|
|
||||||
if (!FromVirtualFile) {
|
|
||||||
printNumberedLine(
|
|
||||||
SM, BufferID,
|
|
||||||
AnnotatedLines[AnnotatedLines.size() - 1].getLineNumber() + 1,
|
|
||||||
lineNumberIndent, Out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
namespace swift {
|
|
||||||
/// Represents one or more annotated file snippets which together form a
|
|
||||||
/// complete diagnostic message.
|
|
||||||
class AnnotatedSourceSnippet {
|
|
||||||
SourceManager &SM;
|
|
||||||
std::map<StringRef, AnnotatedFileExcerpt> FileExcerpts;
|
|
||||||
SmallVector<std::pair<DiagnosticKind, std::string>, 1>
|
|
||||||
UnknownLocationMessages;
|
|
||||||
|
|
||||||
AnnotatedFileExcerpt &excerptForLoc(SourceLoc Loc) {
|
|
||||||
StringRef bufName = SM.getDisplayNameForLoc(Loc);
|
|
||||||
unsigned bufID = SM.findBufferContainingLoc(Loc);
|
|
||||||
// Use the buffer display name as the key in the excerpt map instead of the
|
|
||||||
// buffer identifier to respect #sourceLocation directives.
|
|
||||||
FileExcerpts.emplace(bufName, AnnotatedFileExcerpt(SM, bufID, Loc));
|
|
||||||
return FileExcerpts.find(bufName)->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
AnnotatedSourceSnippet(SourceManager &SM) : SM(SM){};
|
|
||||||
|
|
||||||
void addMessage(SourceLoc Loc, DiagnosticKind Kind, StringRef Message) {
|
|
||||||
if (Loc.isInvalid()) {
|
|
||||||
UnknownLocationMessages.push_back({Kind, Message.str()});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
excerptForLoc(Loc).addMessage(Loc, Kind, Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addHighlight(CharSourceRange Range) {
|
|
||||||
if (Range.isInvalid())
|
|
||||||
return;
|
|
||||||
excerptForLoc(Range.getStart()).addHighlight(Range);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFixIt(CharSourceRange Range, StringRef Text) {
|
|
||||||
if (Range.isInvalid())
|
|
||||||
return;
|
|
||||||
excerptForLoc(Range.getStart()).addFixIt(Range, Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render(raw_ostream &Out) {
|
|
||||||
// Print the excerpt for each file.
|
|
||||||
unsigned lineNumberIndent = 0;
|
|
||||||
if (!FileExcerpts.empty()) {
|
|
||||||
lineNumberIndent = std::max_element(FileExcerpts.begin(), FileExcerpts.end(),
|
|
||||||
[](auto &a, auto &b) {
|
|
||||||
return a.second.getPreferredLineNumberIndent() <
|
|
||||||
b.second.getPreferredLineNumberIndent();
|
|
||||||
})->second.getPreferredLineNumberIndent();
|
|
||||||
}
|
|
||||||
for (auto excerpt : FileExcerpts)
|
|
||||||
excerpt.second.render(lineNumberIndent, Out);
|
|
||||||
|
|
||||||
// Handle messages with invalid locations.
|
|
||||||
if (UnknownLocationMessages.size() == 1) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << "Unknown Location: ";
|
|
||||||
Out.resetColor();
|
|
||||||
printDiagnosticKind(UnknownLocationMessages[0].first, Out);
|
|
||||||
Out << " " << UnknownLocationMessages[0].second << "\n";
|
|
||||||
} else if (UnknownLocationMessages.size() > 1) {
|
|
||||||
Out.changeColor(ColoredStream::Colors::CYAN);
|
|
||||||
Out << "Unknown Location\n";
|
|
||||||
Out.resetColor();
|
|
||||||
for (auto unknownMessage : UnknownLocationMessages) {
|
|
||||||
printEmptyGutter(2, Out);
|
|
||||||
printDiagnosticKind(unknownMessage.first, Out);
|
|
||||||
Out << " " << unknownMessage.second << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace swift
|
|
||||||
|
|
||||||
static void annotateSnippetWithInfo(SourceManager &SM,
|
|
||||||
const DiagnosticInfo &Info,
|
|
||||||
AnnotatedSourceSnippet &Snippet) {
|
|
||||||
llvm::SmallString<256> Text;
|
|
||||||
{
|
|
||||||
llvm::raw_svector_ostream Out(Text);
|
|
||||||
DiagnosticEngine::formatDiagnosticText(Out, Info.FormatString,
|
|
||||||
Info.FormatArgs);
|
|
||||||
// Show associated fix-its as part of the message. This is a
|
|
||||||
// better experience when notes offer a choice of fix-its. It's redundant
|
|
||||||
// for fix-its which are also displayed inline, but helps improve
|
|
||||||
// readability in some situations.
|
|
||||||
if (!Info.FixIts.empty()) {
|
|
||||||
Out << " ";
|
|
||||||
describeFixIts(SM, Info.FixIts, Out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Snippet.addMessage(Info.Loc, Info.Kind, Text);
|
|
||||||
for (auto range : Info.Ranges) {
|
|
||||||
Snippet.addHighlight(range);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't print inline fix-its for notes.
|
|
||||||
if (Info.Kind != DiagnosticKind::Note) {
|
|
||||||
for (auto fixIt : Info.FixIts) {
|
|
||||||
// Don't print multi-line fix-its inline, only include them at the end of
|
|
||||||
// the message.
|
|
||||||
if (!fixIt.getText().contains("\n"))
|
|
||||||
Snippet.addFixIt(fixIt.getRange(), fixIt.getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add any explicitly grouped notes to the snippet.
|
|
||||||
for (auto ChildInfo : Info.ChildDiagnosticInfo) {
|
|
||||||
annotateSnippetWithInfo(SM, *ChildInfo, Snippet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SWIFT_SWIFT_PARSER
|
#if SWIFT_SWIFT_PARSER
|
||||||
/// Enqueue a diagnostic with ASTGen's diagnostic rendering.
|
/// Enqueue a diagnostic with ASTGen's diagnostic rendering.
|
||||||
static void enqueueDiagnostic(
|
static void enqueueDiagnostic(
|
||||||
@@ -1128,33 +478,7 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Use the C++ formatter.
|
// Fall through when we don't have the new diagnostics renderer available.
|
||||||
// FIXME: Once the swift-syntax formatter is enabled everywhere, we will
|
|
||||||
// remove this.
|
|
||||||
if (Info.Loc.isValid()) {
|
|
||||||
if (Info.Kind == DiagnosticKind::Note && currentSnippet) {
|
|
||||||
// If this is a note and we have an in-flight message, add it to that
|
|
||||||
// instead of emitting it separately.
|
|
||||||
annotateSnippetWithInfo(SM, Info, *currentSnippet);
|
|
||||||
} else {
|
|
||||||
// If we encounter a new error/warning/remark, flush any in-flight
|
|
||||||
// snippets.
|
|
||||||
flush(/*includeTrailingBreak*/ true);
|
|
||||||
currentSnippet = std::make_unique<AnnotatedSourceSnippet>(SM);
|
|
||||||
annotateSnippetWithInfo(SM, Info, *currentSnippet);
|
|
||||||
}
|
|
||||||
if (PrintEducationalNotes) {
|
|
||||||
for (auto path : Info.EducationalNotePaths) {
|
|
||||||
if (auto buffer = SM.getFileSystem()->getBufferForFile(path))
|
|
||||||
BufferedEducationalNotes.push_back(buffer->get()->getBuffer().str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall through to print using the LLVM style when there is no source
|
|
||||||
// location.
|
|
||||||
flush(/*includeTrailingBreak*/ false);
|
|
||||||
LLVM_FALLTHROUGH;
|
LLVM_FALLTHROUGH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,21 +502,6 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PrintingDiagnosticConsumer::flush(bool includeTrailingBreak) {
|
void PrintingDiagnosticConsumer::flush(bool includeTrailingBreak) {
|
||||||
if (currentSnippet) {
|
|
||||||
if (ForceColors) {
|
|
||||||
ColoredStream colorStream{Stream};
|
|
||||||
currentSnippet->render(colorStream);
|
|
||||||
if (includeTrailingBreak)
|
|
||||||
colorStream << "\n";
|
|
||||||
} else {
|
|
||||||
NoColorStream noColorStream{Stream};
|
|
||||||
currentSnippet->render(noColorStream);
|
|
||||||
if (includeTrailingBreak)
|
|
||||||
noColorStream << "\n";
|
|
||||||
}
|
|
||||||
currentSnippet.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if SWIFT_SWIFT_PARSER
|
#if SWIFT_SWIFT_PARSER
|
||||||
if (queuedDiagnostics) {
|
if (queuedDiagnostics) {
|
||||||
char *renderedString = nullptr;
|
char *renderedString = nullptr;
|
||||||
@@ -1350,25 +659,3 @@ PrintingDiagnosticConsumer::PrintingDiagnosticConsumer(
|
|||||||
llvm::raw_ostream &stream)
|
llvm::raw_ostream &stream)
|
||||||
: Stream(stream) {}
|
: Stream(stream) {}
|
||||||
PrintingDiagnosticConsumer::~PrintingDiagnosticConsumer() = default;
|
PrintingDiagnosticConsumer::~PrintingDiagnosticConsumer() = default;
|
||||||
|
|
||||||
// FIXME: This implementation is inefficient.
|
|
||||||
std::string SourceManager::getLineString(unsigned BufferID,
|
|
||||||
unsigned LineNumber) {
|
|
||||||
SourceLoc Loc = getLocForLineCol(BufferID, LineNumber, 1);
|
|
||||||
if (Loc.isInvalid())
|
|
||||||
return "";
|
|
||||||
|
|
||||||
auto CurMB = LLVMSourceMgr.getMemoryBuffer(findBufferContainingLoc(Loc));
|
|
||||||
const char *LineStart = Loc.Value.getPointer();
|
|
||||||
const char *BufStart = CurMB->getBufferStart();
|
|
||||||
while (LineStart != BufStart && LineStart[-1] != '\n' &&
|
|
||||||
LineStart[-1] != '\r')
|
|
||||||
--LineStart;
|
|
||||||
|
|
||||||
// Get the end of the line.
|
|
||||||
const char *LineEnd = Loc.Value.getPointer();
|
|
||||||
const char *BufEnd = CurMB->getBufferEnd();
|
|
||||||
while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r')
|
|
||||||
++LineEnd;
|
|
||||||
return std::string(LineStart, LineEnd);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
// RUN: %FileCheck %s --check-prefix CHECK-MACRO-PRINTED < %t/macro-printing.txt
|
// RUN: %FileCheck %s --check-prefix CHECK-MACRO-PRINTED < %t/macro-printing.txt
|
||||||
|
|
||||||
|
// RUN: not %target-swift-frontend -swift-version 5 -typecheck -diagnostic-style=swift -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS %s > %t/pretty-macro-diagnostics.txt 2>&1
|
||||||
|
// RUN: %FileCheck %s --check-prefix PRETTY-DIAGS < %t/pretty-macro-diagnostics.txt
|
||||||
|
|
||||||
// Debug info SIL testing
|
// Debug info SIL testing
|
||||||
// RUN: %target-swift-frontend -swift-version 5 -emit-sil -load-plugin-library %t/%target-library-name(MacroDefinition) %s -module-name MacroUser -o - -g | %FileCheck --check-prefix CHECK-SIL %s
|
// RUN: %target-swift-frontend -swift-version 5 -emit-sil -load-plugin-library %t/%target-library-name(MacroDefinition) %s -module-name MacroUser -o - -g | %FileCheck --check-prefix CHECK-SIL %s
|
||||||
|
|
||||||
@@ -239,6 +242,13 @@ func testNested() {
|
|||||||
// CHECK-DIAGS-NOT: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
// CHECK-DIAGS-NOT: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
||||||
// CHECK-DIAGS: @__swiftmacro_9MacroUser10testNestedyyF9stringifyfMf_9assertAnyfMf_.swift:1:8: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
// CHECK-DIAGS: @__swiftmacro_9MacroUser10testNestedyyF9stringifyfMf_9assertAnyfMf_.swift:1:8: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
||||||
// CHECK-DIAGS-NOT: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
// CHECK-DIAGS-NOT: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
||||||
|
|
||||||
|
// PRETTY-DIAGS: 1:8: error: cannot convert value of type 'Nested' to expected argument type 'Bool'
|
||||||
|
// PRETTY-DIAGS: macro_expand.swift:{{.*}}:39: note: expanded code originates here
|
||||||
|
// PRETTY-DIAGS: ─── macro expansion #stringify
|
||||||
|
// PRETTY-DIAGS: ─── macro expansion #assertAny
|
||||||
|
// PRETTY-DIAGS-NEXT: 1 │ assert(Nested())
|
||||||
|
// PRETTY-DIAGS-NEXT: │ ╰─ error: cannot convert value
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
|
|
||||||
// RUN: %line-directive %s %S/Inputs/extra-newlines-2.swift -- %swiftc_driver -c -diagnostic-style=swift -no-color-diagnostics %s %S/Inputs/extra-newlines-2.swift -module-name Foo 2>&1 | %FileCheck %s
|
// RUN: %line-directive %s %S/Inputs/extra-newlines-2.swift -- %swiftc_driver -c -diagnostic-style=swift -no-color-diagnostics %s %S/Inputs/extra-newlines-2.swift -module-name Foo 2>&1 | %FileCheck %s
|
||||||
|
|
||||||
// UNSUPPORTED: swift_swift_parser
|
// REQUIRES: swift_swift_parser
|
||||||
|
|
||||||
// Check that there are no extra newlines between diagnostics lines
|
// Check that there are no extra newlines between diagnostics lines
|
||||||
|
|
||||||
// CHECK: {{[=]+}} SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}extra-newlines.swift:[[#LINE:]]:5
|
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}extra-newlines.swift:[[#LINE:]]:5
|
||||||
// CHECK-NEXT: [[#LINE-1]] | func foo(a: Int, b: Int) {
|
// CHECK-NEXT: [[#LINE-2]] │
|
||||||
// CHECK-NEXT: [[#LINE]] | a + b
|
// CHECK-NEXT: [[#LINE-1]] │ func foo(a: Int, b: Int) {
|
||||||
// CHECK-NEXT: | ~ ~
|
// CHECK-NEXT: [[#LINE]] │ a + b
|
||||||
// CHECK-NEXT: | ^ warning: result of operator '+' is unused
|
// CHECK-NEXT: │ ╰─ warning: result of operator '+' is unused
|
||||||
// CHECK-NEXT: [[#LINE+1]] | }
|
// CHECK-NEXT: [[#LINE+1]] │ }
|
||||||
|
|
||||||
func foo(a: Int, b: Int) {
|
func foo(a: Int, b: Int) {
|
||||||
a + b
|
a + b
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// RUN: not %target-swift-frontend -diagnostic-style=swift -typecheck %/s 2>&1 | %FileCheck %s
|
// RUN: not %target-swift-frontend -diagnostic-style=swift -typecheck %/s 2>&1 | %FileCheck %s
|
||||||
|
|
||||||
// Note: test has not been updated to match the swift-syntax style.
|
// REQUIRES: swift_swift_parser
|
||||||
// REQUIRES: newswiftsyntaxstyle
|
|
||||||
|
|
||||||
1 + 2
|
1 + 2
|
||||||
|
|
||||||
@@ -70,133 +69,19 @@ foo(b:
|
|||||||
|
|
||||||
// Test fallback for non-ASCII characters.
|
// Test fallback for non-ASCII characters.
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:11
|
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:11
|
||||||
// CHECK: [[#LINE-1]] |
|
// CHECK: [[#LINE-2]] │
|
||||||
// CHECK: [[#LINE]] | let abc = "👍
|
// CHECK: [[#LINE]] │ let abc = "👍
|
||||||
// CHECK: | --> error: unterminated string literal
|
// CHECK: │ ╰─ error: unterminated string literal
|
||||||
// CHECK: [[#LINE+1]] |
|
// CHECK: [[#LINE+1]] │
|
||||||
|
|
||||||
// Test underlining.
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:3
|
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:3
|
||||||
// CHECK: [[#LINE-1]] |
|
// CHECK: [[#LINE-1]] │
|
||||||
// CHECK: [[#LINE]] | 1 + 2
|
// CHECK: [[#LINE]] │ 1 + 2
|
||||||
// CHECK: | ~ ~
|
// CHECK: │ ╰─ warning: result of operator '+' is unused
|
||||||
// CHECK: | ^ warning: result of operator '+' is unused
|
// CHECK: [[#LINE+1]] │
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
// Test inline fix-it rendering.
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:11
|
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:11
|
||||||
// CHECK: [[#LINE-1]] |
|
// CHECK: [[#LINE-1]] │
|
||||||
// CHECK: [[#LINE]] | foo(a: 2, b: 1, a: 2)
|
// CHECK: [[#LINE]] │ foo(b: 1, a: 2)
|
||||||
// CHECK: | ++++++~~~~------
|
// CHECK: │ ╰─ error: argument 'a' must precede argument 'b'
|
||||||
// CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ', a: 2' and insert 'a: 2, ']
|
// CHECK: [[#LINE+1]] │
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:7
|
|
||||||
// CHECK: [[#LINE-2]] | struct Foo {
|
|
||||||
// CHECK: [[#LINE-1]] | var x: Int
|
|
||||||
// CHECK: | ^ note: 'x' previously declared here
|
|
||||||
// CHECK: [[#LINE]] | var x: Int
|
|
||||||
// CHECK: | ^ error: invalid redeclaration of 'x'
|
|
||||||
// CHECK: [[#LINE+1]] | }
|
|
||||||
|
|
||||||
// Test out-of-line fix-its on notes.
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:1
|
|
||||||
// CHECK: [[#LINE-1]] | }
|
|
||||||
// CHECK: [[#LINE]] | bazz()
|
|
||||||
// CHECK: | ~~~~~~
|
|
||||||
// CHECK: | ^ error: call can throw but is not marked with 'try'
|
|
||||||
// CHECK: | ^ note: did you mean to use 'try'? [insert 'try ']
|
|
||||||
// CHECK: | ^ note: did you mean to handle error as optional value? [insert 'try? ']
|
|
||||||
// CHECK: | ^ note: did you mean to disable error propagation? [insert 'try! ']
|
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:7
|
|
||||||
// CHECK: [[#LINE-1]] | extension A {
|
|
||||||
// CHECK: [[#LINE]] | let x: Int = { 42 }
|
|
||||||
// CHECK: | ^ error: extensions must not contain stored properties
|
|
||||||
// CHECK: [[#LINE+1]] | }
|
|
||||||
|
|
||||||
// Test complex out-of-line fix-its.
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:16
|
|
||||||
// CHECK: [[#LINE-1]] | extension A {
|
|
||||||
// CHECK: [[#LINE]] | let x: Int = { 42 }()
|
|
||||||
// CHECK: | ~~~~~~++
|
|
||||||
// CHECK: | ^ error: function produces expected type 'Int'; did you mean to call it with '()'?
|
|
||||||
// CHECK: | ^ note: Remove '=' to make 'x' a computed property [remove '= ' and replace 'let' with 'var']
|
|
||||||
// CHECK: [[#LINE+1]] | }
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:8
|
|
||||||
// CHECK: [[#LINE-1]] |
|
|
||||||
// CHECK: [[#LINE]] | struct B: Decodable {
|
|
||||||
// CHECK: | ^ error: type 'B' does not conform to protocol 'Decodable'
|
|
||||||
// CHECK: [[#LINE+1]] | let a: Foo
|
|
||||||
// CHECK: Swift.Decodable:2:5
|
|
||||||
// CHECK: 1 | public protocol Decodable {
|
|
||||||
// CHECK: 2 | init(from decoder: Decoder) throws
|
|
||||||
// CHECK: | ^ note: protocol requires initializer 'init(from:)' with type 'Decodable'
|
|
||||||
// CHECK: 3 | }
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:14
|
|
||||||
// CHECK: [[#LINE-1]] | // The line below is indented with tabs, not spaces.
|
|
||||||
// CHECK: [[#LINE]] | foo(a: 2, b: 1, a: 2)
|
|
||||||
// CHECK: | ++++++~~~~------
|
|
||||||
// CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ', a: 2' and insert 'a: 2, ']
|
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5
|
|
||||||
// CHECK: [[#LINE-2]] | // Multi-line fix-its
|
|
||||||
// CHECK: [[#LINE-1]] | foo(a: 2, b: 1,
|
|
||||||
// CHECK: | ++++++~~~~-
|
|
||||||
// CHECK: [[#LINE]] | a: 2)
|
|
||||||
// CHECK: | ----
|
|
||||||
// CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ',\n a: 2' and insert 'a: 2, ']
|
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5
|
|
||||||
// CHECK: [[#LINE-3]] |
|
|
||||||
// CHECK: [[#LINE-2]] | foo(b:
|
|
||||||
// CHECK: | ~~
|
|
||||||
// CHECK: [[#LINE-1]] | 1,
|
|
||||||
// CHECK: | ~-
|
|
||||||
// CHECK: [[#LINE]] | a:
|
|
||||||
// CHECK: | --
|
|
||||||
// CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ',\n a:\n 2' and insert 'a:\n 2, ']
|
|
||||||
// CHECK: [[#LINE+1]] | 2)
|
|
||||||
// CHECK: | -
|
|
||||||
// CHECK: [[#LINE+2]] |
|
|
||||||
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5
|
|
||||||
// CHECK: [[#LINE-3]] |
|
|
||||||
// CHECK: [[#LINE-2]] | foo(a: 2, b:
|
|
||||||
// CHECK: | ++++++~~
|
|
||||||
// CHECK: [[#LINE-1]] | 1,
|
|
||||||
// CHECK: | ~-
|
|
||||||
// CHECK: [[#LINE]] | a: 2)
|
|
||||||
// CHECK: | ----
|
|
||||||
// CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ',\n a: 2' and insert 'a: 2, ']
|
|
||||||
// CHECK: [[#LINE+1]] |
|
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5
|
|
||||||
// CHECK: [[#LINE-1]] | func foo(a: Int, b: Int) {
|
|
||||||
// CHECK: [[#LINE]] | a + b
|
|
||||||
// CHECK: | ~ ~
|
|
||||||
// CHECK: | ^ warning: result of operator '+' is unused
|
|
||||||
// CHECK: [[#LINE+1]] | }
|
|
||||||
|
|
||||||
// Test snippet truncation.
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:3
|
|
||||||
// CHECK: [[#LINE-1]] | func baz() {
|
|
||||||
// CHECK: [[#LINE]] | bar(a: "hello, world!")
|
|
||||||
// CHECK: | ^ error: no exact matches in call to global function 'bar'
|
|
||||||
// CHECK: [[#LINE+1]] | }
|
|
||||||
// CHECK: ...
|
|
||||||
// CHECK: [[#LINE:]] |
|
|
||||||
// CHECK: [[#LINE+1]] | func bar(a: Int) {}
|
|
||||||
// CHECK: | ^ note: candidate expects value of type 'Int' for parameter #1
|
|
||||||
// CHECK: [[#LINE+2]] | func bar(a: Float) {}
|
|
||||||
// CHECK: | ^ note: candidate expects value of type 'Float' for parameter #1
|
|
||||||
// CHECK: [[#LINE+3]] |
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// RUN: not %target-swift-frontend -diagnostic-style=swift -typecheck %/s 2>&1 | %FileCheck %s
|
// RUN: not %target-swift-frontend -diagnostic-style=swift -typecheck %/s 2>&1 | %FileCheck %s
|
||||||
|
|
||||||
// Note: test has not been updated to match the swift-syntax style.
|
// REQUIRES: swift_swift_parser
|
||||||
// REQUIRES: newswiftsyntaxstyle
|
// REQUIRES: presumedlocbusted
|
||||||
|
|
||||||
// Error split between the real file and a virtual one.
|
// Error split between the real file and a virtual one.
|
||||||
#sourceLocation(file: "abc.swift", line: 9)
|
#sourceLocation(file: "abc.swift", line: 9)
|
||||||
@@ -40,41 +40,40 @@ let zz: Int = any
|
|||||||
#sourceLocation()
|
#sourceLocation()
|
||||||
|
|
||||||
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-source-loc-directive-diags.swift:[[#LINE:]]:5
|
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-source-loc-directive-diags.swift:[[#LINE:]]:5
|
||||||
// CHECK: [[#LINE-1]] | #sourceLocation()
|
// CHECK: [[#LINE-1]] │ #sourceLocation()
|
||||||
// CHECK: [[#LINE]] | let x = 2
|
// CHECK: [[#LINE]] │ let x = 2
|
||||||
// CHECK: | ^ error: invalid redeclaration of 'x'
|
// CHECK: │ ╰─ error: invalid redeclaration of 'x'
|
||||||
// CHECK: [[#LINE+1]] |
|
// CHECK: [[#LINE+1]] │
|
||||||
// CHECK: abc.swift:9:5
|
// CHECK: abc.swift:9:5
|
||||||
// CHECK: 9 | let x = 1
|
// CHECK: 9 │ let x = 1
|
||||||
// CHECK: | ^ note: 'x' previously declared here
|
// CHECK: │ ╰─ note: 'x' previously declared here
|
||||||
|
|
||||||
|
|
||||||
// CHECK: abc.swift:4:5
|
// CHECK: abc.swift:4:5
|
||||||
// CHECK: 4 | let y = 1
|
// CHECK: 4 │ let y = 1
|
||||||
// CHECK: | ^ note: 'y' previously declared here
|
// CHECK: │ ╰─ note: 'y' previously declared here
|
||||||
// CHECK: xyz.swift:18:5
|
// CHECK: xyz.swift:18:5
|
||||||
// CHECK: 18 | let y = 2
|
// CHECK: 18 │ let y = 2
|
||||||
// CHECK: | ^ error: invalid redeclaration of 'y'
|
// CHECK: │ ╰─ error: invalid redeclaration of 'y'
|
||||||
|
|
||||||
|
|
||||||
// CHECK: abc.swift:3:5
|
// CHECK: abc.swift:3:5
|
||||||
// CHECK: 1 | let z = 1
|
// CHECK: 1 │ let z = 1
|
||||||
// CHECK: | ^ note: 'z' previously declared here
|
// CHECK: │ ╰─ note: 'z' previously declared here
|
||||||
// CHECK: ...
|
// CHECK: ...
|
||||||
// CHECK: 3 | let z = 2
|
// CHECK: 3 │ let z = 2
|
||||||
// CHECK: | ^ error: invalid redeclaration of 'z'
|
// CHECK: │ ╰─ error: invalid redeclaration of 'z'
|
||||||
|
|
||||||
|
|
||||||
// CHECK: abc.swift:10:5
|
// CHECK: abc.swift:10:5
|
||||||
// CHECK: 1 | let a = 1
|
// CHECK: 1 │ let a = 1
|
||||||
// CHECK: | ^ note: 'a' previously declared here
|
// CHECK: │ ╰─ note: 'a' previously declared here
|
||||||
// CHECK: ...
|
// CHECK: ...
|
||||||
// CHECK: 10 | let a = 2
|
// CHECK: 10 │ let a = 2
|
||||||
// CHECK: | ^ error: invalid redeclaration of 'a'
|
// CHECK: │ ╰─ error: invalid redeclaration of 'a'
|
||||||
|
|
||||||
|
|
||||||
// CHECK: abc.swift:2:15
|
// CHECK: abc.swift:2:15
|
||||||
// CHECK: 2 | let zz: Int = any as! Int
|
// CHECK: 2 │ let zz: Int = any as! Int
|
||||||
// CHECK: | ~~~++++++++
|
// CHECK: │ ╰─ error: cannot convert value of type 'Any' to specified type 'Int' [insert ' as! Int']
|
||||||
// CHECK: | ^ error: cannot convert value of type 'Any' to specified type 'Int' [insert ' as! Int']
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user