Add CharSourceRange -- a half-open character range, which will be used in IDE

integration

Motivation: libIDE clients should be simple, and they should not have to
translate token-based SourceRanges to character locations.

This also allows us to remove the dependency of DiagnosticConsumer on the
Lexer.  Now the DiagnosticEngine translates the diagnostics to CharSourceRanges
and passes character-based ranges to the DiagnosticConsumer.


Swift SVN r7173
This commit is contained in:
Dmitri Hrybenko
2013-08-12 20:15:51 +00:00
parent 67affb462a
commit 70f2b64ad9
14 changed files with 166 additions and 108 deletions

View File

@@ -180,13 +180,12 @@ namespace swift {
/// the DiagnosticArguments that it requires.
class Diagnostic {
public:
typedef DiagnosticInfo::Range Range;
typedef DiagnosticInfo::FixIt FixIt;
private:
DiagID ID;
SmallVector<DiagnosticArgument, 3> Args;
SmallVector<Range, 2> Ranges;
SmallVector<CharSourceRange, 2> Ranges;
SmallVector<FixIt, 2> FixIts;
public:
@@ -207,10 +206,10 @@ namespace swift {
// Accessors.
DiagID getID() const { return ID; }
ArrayRef<DiagnosticArgument> getArgs() const { return Args; }
ArrayRef<Range> getRanges() const { return Ranges; }
ArrayRef<CharSourceRange> getRanges() const { return Ranges; }
ArrayRef<FixIt> getFixIts() const { return FixIts; }
void addRange(Range R) {
void addRange(CharSourceRange R) {
Ranges.push_back(R);
}
@@ -266,30 +265,37 @@ namespace swift {
/// \brief Flush the active diagnostic to the diagnostic output engine.
void flush();
/// \brief Add a range to the currently-active diagnostic.
///
/// Use a SourceRange for a token-based range; explicitly construct a
/// Diagnostic::Range from two SourceLocs for a character-based range.
InFlightDiagnostic &highlight(Diagnostic::Range R);
/// \brief Add a token-based range to the currently-active diagnostic.
InFlightDiagnostic &highlight(SourceRange R);
/// \brief Add a replacement fix-it to the currently-active diagnostic.
///
/// Use a SourceRange for a token-based range; explicitly construct a
/// Diagnostic::Range from two SourceLocs for a character-based range.
InFlightDiagnostic &fixItReplace(Diagnostic::Range R, StringRef Str);
/// \brief Add a character-based range to the currently-active diagnostic.
InFlightDiagnostic &highlightChars(SourceLoc Start, SourceLoc End);
/// \brief Add a token-based replacement fix-it to the currently-active
/// diagnostic.
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str);
/// \brief Add a character-based replacement fix-it to the currently-active
/// diagnostic.
InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End,
StringRef Str);
/// \brief Add an insertion fix-it to the currently-active diagnostic.
InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef Str) {
return fixItReplace(Diagnostic::Range(L, L), Str);
return fixItReplaceChars(L, L, Str);
}
/// \brief Add a removal fix-it to the currently-active diagnostic.
///
/// Use a SourceRange for a token-based range; explicitly construct a
/// Diagnostic::Range from two SourceLocs for a character-based range.
InFlightDiagnostic &fixItRemove(Diagnostic::Range R) {
/// \brief Add a token-based removal fix-it to the currently-active
/// diagnostic.
InFlightDiagnostic &fixItRemove(SourceRange R) {
return fixItReplace(R, {});
}
/// \brief Add a token-based removal fix-it to the currently-active
/// diagnostic.
InFlightDiagnostic &fixItRemoveChars(SourceLoc Start, SourceLoc End) {
return fixItReplaceChars(Start, End, {});
}
};
/// \brief Class responsible for formatting diagnostics and presenting them
@@ -475,22 +481,6 @@ namespace swift {
/// \brief Retrieve the active diagnostic.
Diagnostic &getActiveDiagnostic() { return *ActiveDiagnostic; }
};
inline InFlightDiagnostic &
InFlightDiagnostic::highlight(Diagnostic::Range R) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic().addRange(R);
return *this;
}
inline InFlightDiagnostic &
InFlightDiagnostic::fixItReplace(Diagnostic::Range R, StringRef Str) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(R, Str));
return *this;
}
} // end namespace swift
#endif

View File

@@ -40,42 +40,21 @@ enum class DiagnosticKind {
/// \brief Extra information carried along with a diagnostic, which may or
/// may not be of interest to a given diagnostic consumer.
struct DiagnosticInfo {
/// Represents a source range that can either be a half-open character range
/// (like llvm::SMRange) or a closed token range (like SourceRange).
///
/// Diagnostics need to be able to handle both kinds of ranges.
class Range {
public:
SourceLoc Start, End;
bool IsTokenRange;
Range(SourceLoc S, SourceLoc E, bool TokenRange = false)
: Start(S), End(E), IsTokenRange(TokenRange) {}
/// Force callers to choose whether they want a zero-length range at \p S,
/// a one-character range at \p S, or a range consisting of the token at
/// \p S.
/*implicit*/ Range(SourceLoc S) = delete;
/*implicit*/ Range(SourceRange SR)
: Start(SR.Start), End(SR.End), IsTokenRange(true) {}
};
/// Represents a fix-it, a replacement of one range of text with another.
class FixIt {
DiagnosticInfo::Range Range;
CharSourceRange Range;
std::string Text;
public:
FixIt(DiagnosticInfo::Range R, StringRef Str)
: Range(R), Text(Str) {}
FixIt(CharSourceRange R, StringRef Str)
: Range(R), Text(Str) {}
DiagnosticInfo::Range getRange() const { return Range; }
CharSourceRange getRange() const { return Range; }
StringRef getText() const { return Text; }
};
/// \brief Extra source ranges that are attached to the diagnostic.
ArrayRef<Range> Ranges;
ArrayRef<CharSourceRange> Ranges;
/// \brief Extra source ranges that are attached to the diagnostic.
ArrayRef<FixIt> FixIts;
@@ -88,7 +67,9 @@ protected:
// we need to translate swift::SourceLoc to llvm::SMLoc.
static llvm::SMLoc getRawLoc(SourceLoc Loc) { return Loc.Value; }
static llvm::SMRange getRawRange(SourceManager &SM, DiagnosticInfo::Range R);
static llvm::SMRange getRawRange(SourceManager &SM, CharSourceRange R) {
return llvm::SMRange(getRawLoc(R.getStart()), getRawLoc(R.getEnd()));
}
static llvm::SMFixIt getRawFixIt(SourceManager &SM, DiagnosticInfo::FixIt F) {
// FIXME: It's unfortunate that we have to copy the replacement text.

View File

@@ -101,6 +101,37 @@ public:
void dump(const SourceManager &SM) const;
};
/// A half-open character-based source range.
class CharSourceRange {
SourceLoc Start;
unsigned ByteLength;
public:
/// \brief Constructs an invalid range.
CharSourceRange() {}
/// \brief Constructs a character range which starts and ends at the
/// specified character locations.
CharSourceRange(SourceManager &SM, SourceLoc Start, SourceLoc End);
bool isValid() const { return Start.isValid(); }
bool isInvalid() const { return Start.isInvalid(); }
SourceLoc getStart() const { return Start; }
SourceLoc getEnd() const {
if (Start.isValid())
return Start.getAdvancedLoc(ByteLength);
else
return SourceLoc();
}
/// \brief Return the length of this valid range in bytes. Can be zero.
unsigned getByteLength() const {
assert(isValid() && "length does not make sense for an invalid range");
return ByteLength;
}
};
} // end namespace swift
#endif

View File

@@ -107,6 +107,9 @@ public:
/// \brief Returns the offset in bytes for the given source location.
unsigned getLocOffsetInBuffer(SourceLoc Loc, unsigned BufferID) const;
/// \brief Returns the distance in bytes between the given source locations.
unsigned getByteDistance(SourceLoc Start, SourceLoc End) const;
std::pair<unsigned, unsigned> getLineAndColumn(SourceLoc Loc,
int BufferID = -1) const {
assert(Loc.isValid());

View File

@@ -22,6 +22,7 @@
#include "swift/AST/PrintOptions.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Parse/Lexer.h" // bad dependency
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
@@ -49,6 +50,51 @@ static StoredDiagnosticInfo StoredDiagnosticInfos[] = {
{ DiagnosticKind::Error, "<not a diagnostic>" }
};
static CharSourceRange toCharSourceRange(SourceManager &SM, SourceRange SR) {
return CharSourceRange(SM, SR.Start, Lexer::getLocForEndOfToken(SM, SR.End));
}
static CharSourceRange toCharSourceRange(SourceManager &SM, SourceLoc Start,
SourceLoc End) {
return CharSourceRange(SM, Start, End);
}
InFlightDiagnostic &InFlightDiagnostic::highlight(SourceRange R) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic()
.addRange(toCharSourceRange(Engine->SourceMgr, R));
return *this;
}
InFlightDiagnostic &InFlightDiagnostic::highlightChars(SourceLoc Start,
SourceLoc End) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic()
.addRange(toCharSourceRange(Engine->SourceMgr, Start, End));
return *this;
}
InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
StringRef Str) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic().addFixIt(
Diagnostic::FixIt(toCharSourceRange(Engine->SourceMgr, R), Str));
return *this;
}
InFlightDiagnostic &InFlightDiagnostic::fixItReplaceChars(SourceLoc Start,
SourceLoc End,
StringRef Str) {
assert(IsActive && "Cannot modify an inactive diagnostic");
if (Engine)
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(
toCharSourceRange(Engine->SourceMgr, Start, End), Str));
return *this;
}
void InFlightDiagnostic::flush() {
if (!IsActive)
return;
@@ -373,3 +419,4 @@ void DiagnosticEngine::flushActiveDiagnostic() {
// Reset the active diagnostic.
ActiveDiagnostic.reset();
}

View File

@@ -15,7 +15,6 @@
//===----------------------------------------------------------------------===//
#include "swift/Basic/DiagnosticConsumer.h"
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -24,18 +23,6 @@ using namespace swift;
DiagnosticConsumer::~DiagnosticConsumer() { }
llvm::SMRange DiagnosticConsumer::getRawRange(SourceManager &SM,
DiagnosticInfo::Range R) {
SourceLoc End;
if (R.IsTokenRange)
End = Lexer::getLocForEndOfToken(SM, R.End);
else
End = R.End;
return llvm::SMRange(getRawLoc(R.Start), getRawLoc(End));
}
void NullDiagnosticConsumer::handleDiagnostic(SourceManager &SM,
SourceLoc Loc,
DiagnosticKind Kind,

View File

@@ -36,6 +36,19 @@ unsigned SourceManager::getLocOffsetInBuffer(SourceLoc Loc,
return Loc.Value.getPointer() - Buffer->getBuffer().begin();
}
unsigned SourceManager::getByteDistance(SourceLoc Start, SourceLoc End) const {
#ifndef NDEBUG
unsigned BufferID = findBufferContainingLoc(Start);
auto *Buffer = LLVMSourceMgr.getMemoryBuffer(BufferID);
assert(End.Value.getPointer() >= Buffer->getBuffer().begin() &&
End.Value.getPointer() <= Buffer->getBuffer().end() &&
"End location is not from the same buffer");
#endif
// When we have a rope buffer, could be implemented in terms of
// getLocOffsetInBuffer().
return End.Value.getPointer() - Start.Value.getPointer();
}
void SourceLoc::printLineAndColumn(raw_ostream &OS,
const SourceManager &SM) const {
if (isInvalid()) {
@@ -93,3 +106,12 @@ void SourceRange::dump(const SourceManager &SM) const {
print(llvm::errs(), SM);
}
CharSourceRange::CharSourceRange(SourceManager &SM, SourceLoc Start,
SourceLoc End)
: Start(Start) {
assert(Start.isValid() == End.isValid() &&
"Start and end should either both be valid or both be invalid!");
if (Start.isValid())
ByteLength = SM.getByteDistance(Start, End);
}

View File

@@ -129,14 +129,14 @@ static bool checkForFixIt(const ExpectedFixIt &Expected,
}
/// VerifyDiagnostics - 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.
/// \brief 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.
bool DiagnosticVerifier::verifyFile(unsigned BufferID) {
const SourceLoc BufferStartLoc = SM.getLocForBufferStart(BufferID);
const llvm::MemoryBuffer &MemBuffer = *SM->getMemoryBuffer(BufferID);
StringRef InputFile = MemBuffer.getBuffer();
// Queue up all of the diagnostics, allowing us to sort them and emit them in
// file order.
std::vector<std::pair<const char *, std::string> > Errors;
@@ -210,9 +210,10 @@ bool DiagnosticVerifier::verifyFile(unsigned BufferID) {
if (PrevExpectedContinuationLine)
Expected.LineNo = PrevExpectedContinuationLine;
else
Expected.LineNo =
SM->FindLineNumber(llvm::SMLoc::getFromPointer(MatchStart.data()),
BufferID);
Expected.LineNo = SM.getLineAndColumn(
BufferStartLoc
.getAdvancedLoc(MatchStart.data() - MemBuffer.getBufferStart()),
BufferID).first;
// Check if the next expected diagnostic should be in the same line.
StringRef AfterEnd = MatchStart.substr(End + strlen("}}"));

View File

@@ -43,7 +43,7 @@ PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM, SourceLoc Loc,
// Translate ranges.
SmallVector<llvm::SMRange, 2> Ranges;
for (DiagnosticInfo::Range R : Info.Ranges)
for (auto R : Info.Ranges)
Ranges.push_back(getRawRange(SM, R));
// Translate fix-its.

View File

@@ -267,7 +267,7 @@ static void diagnoseEmbeddedNul(DiagnosticEngine *Diags, const char *Ptr) {
SourceLoc NulLoc = Lexer::getSourceLoc(Ptr);
SourceLoc NulEndLoc = Lexer::getSourceLoc(Ptr+1);
Diags->diagnose(NulLoc, diag::lex_nul_character)
.fixItRemove(DiagnosticInfo::Range(NulLoc, NulEndLoc));
.fixItRemoveChars(NulLoc, NulEndLoc);
}
void Lexer::skipToEndOfLine() {
@@ -1036,8 +1036,7 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
if (Diags)
Diags->diagnose(Lexer::getSourceLoc(CurPtr - 1),
diag::lex_unexpected_quote_string_interpolation)
.highlight(Diagnostic::Range(InterpStart,
Lexer::getSourceLoc(CurPtr-1)));
.highlightChars(InterpStart, Lexer::getSourceLoc(CurPtr-1));
return CurPtr-1;
case 0:
// If we hit EOF, we fail.

View File

@@ -1309,7 +1309,7 @@ FuncDecl *Parser::parseDeclFunc(SourceLoc StaticLoc, unsigned Flags) {
// Reject 'static' functions at global scope.
if (StaticLoc.isValid() && !HasContainerType) {
diagnose(Tok, diag::static_func_decl_global_scope)
.fixItRemove(Diagnostic::Range(StaticLoc, Tok.getLoc()));
.fixItRemoveChars(StaticLoc, Tok.getLoc());
StaticLoc = SourceLoc();
}

View File

@@ -467,7 +467,7 @@ NullablePtr<Expr> Parser::parseExprUnary(Diag<> Message) {
assert(OperEndLoc != Tok.getLoc() && "binary operator with no spaces?");
diagnose(PreviousLoc, diag::expected_prefix_operator)
.fixItRemove(Diagnostic::Range(OperEndLoc, Tok.getLoc()));
.fixItRemoveChars(OperEndLoc, Tok.getLoc());
break;
}
}

View File

@@ -119,7 +119,7 @@ static void checkInheritanceClause(TypeChecker &tc, Decl *decl) {
tc.diagnose(inherited.getSourceRange().Start,
diag::duplicate_inheritance, inheritedTy)
.fixItRemove(Diagnostic::Range(afterPriorLoc, afterMyEndLoc))
.fixItRemoveChars(afterPriorLoc, afterMyEndLoc)
.highlight(knownType->second);
continue;
}
@@ -175,7 +175,7 @@ static void checkInheritanceClause(TypeChecker &tc, Decl *decl) {
tc.diagnose(inherited.getSourceRange().Start,
diag::superclass_not_first, inheritedTy)
.fixItRemove(Diagnostic::Range(afterPriorLoc, afterMyEndLoc))
.fixItRemoveChars(afterPriorLoc, afterMyEndLoc)
.fixItInsert(inheritedClause[0].getSourceRange().Start,
inheritedTy.getString() + ", ");

View File

@@ -176,12 +176,12 @@ private:
unsigned getEmitFile(StringRef Filename);
/// \brief Add a source location to a record.
void addLocToRecord(llvm::SMLoc Loc,
void addLocToRecord(SourceLoc Loc,
SourceManager &SM,
StringRef Filename,
RecordDataImpl &Record);
void addRangeToRecord(llvm::SMRange Range, SourceManager &SM,
void addRangeToRecord(CharSourceRange Range, SourceManager &SM,
StringRef Filename, RecordDataImpl &Record);
/// \brief Emit the message payload of a diagnostic to bitcode.
@@ -221,11 +221,10 @@ unsigned SerializedDiagnosticConsumer::getEmitFile(StringRef Filename) {
return entry;
}
void SerializedDiagnosticConsumer::addLocToRecord(llvm::SMLoc Loc,
void SerializedDiagnosticConsumer::addLocToRecord(SourceLoc Loc,
SourceManager &SM,
StringRef Filename,
RecordDataImpl &Record)
{
RecordDataImpl &Record) {
if (!Loc.isValid()) {
// Emit a "sentinel" location.
Record.push_back((unsigned)0); // File.
@@ -236,7 +235,7 @@ void SerializedDiagnosticConsumer::addLocToRecord(llvm::SMLoc Loc,
}
unsigned line, col;
std::tie(line, col) = SM->getLineAndColumn(Loc);
std::tie(line, col) = SM.getLineAndColumn(Loc);
Record.push_back(getEmitFile(Filename));
Record.push_back(line);
@@ -244,12 +243,12 @@ void SerializedDiagnosticConsumer::addLocToRecord(llvm::SMLoc Loc,
Record.push_back(0);
}
void SerializedDiagnosticConsumer::addRangeToRecord(llvm::SMRange Range,
void SerializedDiagnosticConsumer::addRangeToRecord(CharSourceRange Range,
SourceManager &SM,
StringRef Filename,
RecordDataImpl &Record) {
addLocToRecord(Range.Start, SM, Filename, Record);
addLocToRecord(Range.End, SM, Filename, Record);
addLocToRecord(Range.getStart(), SM, Filename, Record);
addLocToRecord(Range.getEnd(), SM, Filename, Record);
}
/// \brief Map a Swift DiagosticKind to the diagnostic level expected
@@ -461,7 +460,7 @@ emitDiagnosticMessage(SourceManager &SM,
Record.clear();
Record.push_back(RECORD_DIAG);
Record.push_back(getDiagnosticLevel(Kind));
addLocToRecord(getRawLoc(Loc), SM, D.getFilename(), Record);
addLocToRecord(Loc, SM, D.getFilename(), Record);
// FIXME: Swift diagnostics currently have no category.
Record.push_back(0);
@@ -485,21 +484,19 @@ emitDiagnosticMessage(SourceManager &SM,
for (const auto &R : Info.Ranges) {
State->Record.clear();
State->Record.push_back(RECORD_SOURCE_RANGE);
auto RawRange = getRawRange(SM, R);
addRangeToRecord(RawRange, SM, D.getFilename(), State->Record);
addRangeToRecord(R, SM, D.getFilename(), State->Record);
State->Stream.EmitRecordWithAbbrev(RangeAbbrev, State->Record);
}
// Emit FixIts.
auto FixItAbbrev = State->Abbrevs.get(RECORD_FIXIT);
for (const auto &F : Info.FixIts) {
auto RawFixIt = getRawFixIt(SM, F);
if (RawFixIt.getRange().isValid()) {
if (F.getRange().isValid()) {
State->Record.clear();
State->Record.push_back(RECORD_FIXIT);
addRangeToRecord(RawFixIt.getRange(), SM, D.getFilename(), State->Record);
State->Record.push_back(RawFixIt.getText().size());
Stream.EmitRecordWithBlob(FixItAbbrev, Record, RawFixIt.getText());
addRangeToRecord(F.getRange(), SM, D.getFilename(), State->Record);
State->Record.push_back(F.getText().size());
Stream.EmitRecordWithBlob(FixItAbbrev, Record, F.getText());
}
}
}