mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
introduce a simple suppression mechanism into the type-coercion code, which is the only place we should need it. Swift SVN r1388
245 lines
7.6 KiB
C++
245 lines
7.6 KiB
C++
//===- DiagnosticEngine.h - Diagnostic Display Engine -----------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the DiagnosticEngine class, which manages any diagnostics
|
|
// emitted by Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/DiagnosticEngine.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
using namespace swift;
|
|
|
|
struct StoredDiagnosticInfo {
|
|
/// \brief The kind of diagnostic we're dealing with.
|
|
DiagnosticKind Kind;
|
|
|
|
// FIXME: Category
|
|
|
|
/// \brief Text associated with the diagnostic
|
|
const char *Text;
|
|
};
|
|
|
|
static StoredDiagnosticInfo StoredDiagnosticInfos[] = {
|
|
#define ERROR(ID,Category,Options,Text,Signature) \
|
|
{ DiagnosticKind::Error, Text },
|
|
#define WARNING(ID,Category,Options,Text,Signature) \
|
|
{ DiagnosticKind::Warning, Text },
|
|
#define NOTE(ID,Category,Options,Text,Signature) \
|
|
{ DiagnosticKind::Note, Text },
|
|
#include "swift/AST/Diagnostics.def"
|
|
{ DiagnosticKind::Error, "<not a diagnostic>" }
|
|
};
|
|
|
|
void InFlightDiagnostic::flush() {
|
|
if (!IsActive)
|
|
return;
|
|
|
|
IsActive = false;
|
|
if (Engine)
|
|
Engine->flushActiveDiagnostic();
|
|
}
|
|
|
|
/// \brief Skip forward to one of the given delimiters.
|
|
///
|
|
/// \param Text The text to search through, which will be updated to point
|
|
/// just after the delimiter.
|
|
///
|
|
/// \param Delim1 The first character delimiter to search for.
|
|
///
|
|
/// \param Delim2 The second character delimiter to search for.
|
|
///
|
|
/// \returns The string leading up to the delimiter, or the empty string
|
|
/// if no delimiter is found.
|
|
static StringRef
|
|
skipToDelimiter(StringRef &Text, char Delim1, char Delim2 = 0) {
|
|
unsigned Depth = 0;
|
|
|
|
unsigned I = 0;
|
|
for (unsigned N = Text.size(); I != N; ++I) {
|
|
if (Depth == 0 && Text[I] == '{') {
|
|
++Depth;
|
|
continue;
|
|
}
|
|
if (Depth > 0 && Text[I] == '}') {
|
|
--Depth;
|
|
continue;
|
|
}
|
|
|
|
if (Text[I] == Delim1 || Text[I] == Delim2)
|
|
break;
|
|
}
|
|
|
|
assert(Depth == 0 && "Unbalanced {} set in diagnostic text");
|
|
StringRef Result = Text.substr(0, I);
|
|
Text = Text.substr(I + 1);
|
|
return Result;
|
|
}
|
|
|
|
/// \brief Format a selection argument and write it to the given stream.
|
|
static void formatSelectionArgument(StringRef ModifierArguments,
|
|
ArrayRef<DiagnosticArgument> Args,
|
|
unsigned SelectedIndex,
|
|
llvm::raw_ostream &Out) {
|
|
do {
|
|
StringRef Text = skipToDelimiter(ModifierArguments, '|');
|
|
if (SelectedIndex == 0) {
|
|
Out << Text;
|
|
break;
|
|
}
|
|
--SelectedIndex;
|
|
} while (true);
|
|
|
|
}
|
|
|
|
/// \brief Format a single diagnostic argument and write it to the given
|
|
/// stream.
|
|
static void formatDiagnosticArgument(StringRef Modifier,
|
|
StringRef ModifierArguments,
|
|
ArrayRef<DiagnosticArgument> Args,
|
|
unsigned ArgIndex,
|
|
llvm::raw_ostream &Out) {
|
|
const DiagnosticArgument &Arg = Args[ArgIndex];
|
|
switch (Arg.getKind()) {
|
|
case DiagnosticArgumentKind::Integer:
|
|
if (Modifier == "select") {
|
|
assert(Arg.getAsInteger() >= 0 && "Negative selection index");
|
|
formatSelectionArgument(ModifierArguments, Args, Arg.getAsInteger(),
|
|
Out);
|
|
} else {
|
|
assert(Modifier.empty() && "Improper modifier for integer argument");
|
|
Out << Arg.getAsInteger();
|
|
}
|
|
break;
|
|
|
|
case DiagnosticArgumentKind::Unsigned:
|
|
if (Modifier == "select") {
|
|
formatSelectionArgument(ModifierArguments, Args, Arg.getAsUnsigned(),
|
|
Out);
|
|
} else {
|
|
assert(Modifier.empty() && "Improper modifier for unsigned argument");
|
|
Out << Arg.getAsUnsigned();
|
|
}
|
|
break;
|
|
|
|
case DiagnosticArgumentKind::String:
|
|
assert(Modifier.empty() && "Improper modifier for string argument");
|
|
Out << Arg.getAsString();
|
|
break;
|
|
|
|
case DiagnosticArgumentKind::Identifier:
|
|
assert(Modifier.empty() && "Improper modifier for identifier argument");
|
|
Out << '\'' << Arg.getAsIdentifier() << '\'';
|
|
break;
|
|
case DiagnosticArgumentKind::Type:
|
|
assert(Modifier.empty() && "Improper modifier for Type argument");
|
|
Out << '\'' << Arg.getAsType().getString() << '\'';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// \brief Format the given diagnostic text and place the result in the given
|
|
/// buffer.
|
|
static void formatDiagnosticText(StringRef InText,
|
|
ArrayRef<DiagnosticArgument> Args,
|
|
llvm::SmallVectorImpl<char> &OutText) {
|
|
llvm::raw_svector_ostream Out(OutText);
|
|
while (!InText.empty()) {
|
|
size_t Percent = InText.find('%');
|
|
if (Percent == StringRef::npos) {
|
|
// Write the rest of the string; we're done.
|
|
Out.write(InText.data(), InText.size());
|
|
break;
|
|
}
|
|
|
|
// Write the string up to (but not including) the %, then drop that text
|
|
// (including the %).
|
|
Out.write(InText.data(), Percent);
|
|
InText = InText.substr(Percent + 1);
|
|
|
|
// '%%' -> '%'.
|
|
if (InText[0] == '%') {
|
|
Out.write('%');
|
|
InText = InText.substr(1);
|
|
continue;
|
|
}
|
|
|
|
// Parse an optional modifier.
|
|
StringRef Modifier;
|
|
{
|
|
unsigned Length = 0;
|
|
while (isalpha(InText[Length]))
|
|
++Length;
|
|
Modifier = InText.substr(0, Length);
|
|
InText = InText.substr(Length);
|
|
}
|
|
|
|
// Parse the optional argument list for a modifier, which is brace-enclosed.
|
|
StringRef ModifierArguments;
|
|
if (InText[0] == '{') {
|
|
InText = InText.substr(1);
|
|
ModifierArguments = skipToDelimiter(InText, '}');
|
|
}
|
|
|
|
// Find the digit sequence.
|
|
unsigned Length = 0;
|
|
for (size_t N = InText.size(); Length != N; ++Length) {
|
|
if (!isdigit(InText[Length]))
|
|
break;
|
|
}
|
|
|
|
// Parse the digit sequence into an argument index.
|
|
unsigned ArgIndex;
|
|
bool Result = InText.substr(0, Length).getAsInteger(10, ArgIndex);
|
|
assert(!Result && "Unparseable argument index value?");
|
|
(void)Result;
|
|
assert(ArgIndex < Args.size() && "Out-of-range argument index");
|
|
InText = InText.substr(Length);
|
|
|
|
// Convert the argument to a string.
|
|
formatDiagnosticArgument(Modifier, ModifierArguments, Args, ArgIndex, Out);
|
|
}
|
|
}
|
|
|
|
void DiagnosticEngine::flushActiveDiagnostic() {
|
|
assert(ActiveDiagnostic && "No active diagnostic to flush");
|
|
const StoredDiagnosticInfo &StoredInfo
|
|
= StoredDiagnosticInfos[(unsigned)ActiveDiagnostic->getID()];
|
|
|
|
// Check whether this is an error.
|
|
switch (StoredInfo.Kind) {
|
|
case DiagnosticKind::Error:
|
|
HadAnyError = true;
|
|
break;
|
|
|
|
case DiagnosticKind::Note:
|
|
case DiagnosticKind::Warning:
|
|
break;
|
|
}
|
|
|
|
// Actually substitute the diagnostic arguments into the diagnostic text.
|
|
llvm::SmallString<256> Text;
|
|
formatDiagnosticText(StoredInfo.Text, ActiveDiagnostic->getArgs(), Text);
|
|
|
|
// Pass the diagnostic off to the consumer.
|
|
DiagnosticInfo Info;
|
|
Info.Ranges = ActiveDiagnostic->getRanges();
|
|
Consumer.handleDiagnostic(SourceMgr, ActiveDiagnosticLoc, StoredInfo.Kind,
|
|
Text, Info);
|
|
|
|
// Reset the active diagnostic.
|
|
ActiveDiagnostic.reset();
|
|
}
|