blob: 58bdc83f3f6ec728c305faa2a310e6eacda8c482 [file] [log] [blame]
//===--- SourceLoc.h - Source Locations and Ranges --------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines types used to reason about source locations and ranges.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_SOURCELOC_H
#define SWIFT_BASIC_SOURCELOC_H
#include "swift/Basic/Debug.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/SMLoc.h"
#include <functional>
namespace swift {
class SourceManager;
/// SourceLoc in swift is just an SMLoc. We define it as a different type
/// (instead of as a typedef) just to remove the "getFromPointer" methods and
/// enforce purity in the Swift codebase.
class SourceLoc {
friend class SourceManager;
friend class SourceRange;
friend class CharSourceRange;
friend class DiagnosticConsumer;
llvm::SMLoc Value;
public:
SourceLoc() {}
explicit SourceLoc(llvm::SMLoc Value) : Value(Value) {}
bool isValid() const { return Value.isValid(); }
bool isInvalid() const { return !isValid(); }
bool operator==(const SourceLoc &RHS) const { return RHS.Value == Value; }
bool operator!=(const SourceLoc &RHS) const { return !operator==(RHS); }
/// Return a source location advanced a specified number of bytes.
SourceLoc getAdvancedLoc(int ByteOffset) const {
assert(isValid() && "Can't advance an invalid location");
return SourceLoc(
llvm::SMLoc::getFromPointer(Value.getPointer() + ByteOffset));
}
SourceLoc getAdvancedLocOrInvalid(int ByteOffset) const {
if (isValid())
return getAdvancedLoc(ByteOffset);
return SourceLoc();
}
const void *getOpaquePointerValue() const { return Value.getPointer(); }
/// Print out the SourceLoc. If this location is in the same buffer
/// as specified by \c LastBufferID, then we don't print the filename. If
/// not, we do print the filename, and then update \c LastBufferID with the
/// BufferID printed.
void print(raw_ostream &OS, const SourceManager &SM,
unsigned &LastBufferID) const;
void printLineAndColumn(raw_ostream &OS, const SourceManager &SM,
unsigned BufferID = 0) const;
void print(raw_ostream &OS, const SourceManager &SM) const {
unsigned Tmp = ~0U;
print(OS, SM, Tmp);
}
SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM));
friend size_t hash_value(SourceLoc loc) {
return reinterpret_cast<uintptr_t>(loc.getOpaquePointerValue());
}
friend void simple_display(raw_ostream &OS, const SourceLoc &loc) {
// Nothing meaningful to print.
}
};
/// SourceRange in swift is a pair of locations. However, note that the end
/// location is the start of the last token in the range, not the last character
/// in the range. This is unlike SMRange, so we use a distinct type to make
/// sure that proper conversions happen where important.
class SourceRange {
public:
SourceLoc Start, End;
SourceRange() {}
SourceRange(SourceLoc Loc) : Start(Loc), End(Loc) {}
SourceRange(SourceLoc Start, SourceLoc End) : Start(Start), End(End) {
assert(Start.isValid() == End.isValid() &&
"Start and end should either both be valid or both be invalid!");
}
bool isValid() const { return Start.isValid(); }
bool isInvalid() const { return !isValid(); }
/// Extend this SourceRange to the smallest continuous SourceRange that
/// includes both this range and the other one.
void widen(SourceRange Other);
bool operator==(const SourceRange &other) const {
return Start == other.Start && End == other.End;
}
bool operator!=(const SourceRange &other) const { return !operator==(other); }
/// Print out the SourceRange. If the locations are in the same buffer
/// as specified by LastBufferID, then we don't print the filename. If not,
/// we do print the filename, and then update LastBufferID with the BufferID
/// printed.
void print(raw_ostream &OS, const SourceManager &SM,
unsigned &LastBufferID, bool PrintText = true) const;
void print(raw_ostream &OS, const SourceManager &SM,
bool PrintText = true) const {
unsigned Tmp = ~0U;
print(OS, SM, Tmp, PrintText);
}
SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM));
};
/// A half-open character-based source range.
class CharSourceRange {
SourceLoc Start;
unsigned ByteLength;
public:
/// Constructs an invalid range.
CharSourceRange() = default;
CharSourceRange(SourceLoc Start, unsigned ByteLength)
: Start(Start), ByteLength(ByteLength) {}
/// Constructs a character range which starts and ends at the
/// specified character locations.
CharSourceRange(const SourceManager &SM, SourceLoc Start, SourceLoc End);
/// Use Lexer::getCharSourceRangeFromSourceRange() instead.
CharSourceRange(const SourceManager &SM, SourceRange Range) = delete;
bool isValid() const { return Start.isValid(); }
bool isInvalid() const { return !isValid(); }
bool operator==(const CharSourceRange &other) const {
return Start == other.Start && ByteLength == other.ByteLength;
}
bool operator!=(const CharSourceRange &other) const {
return !operator==(other);
}
SourceLoc getStart() const { return Start; }
SourceLoc getEnd() const { return Start.getAdvancedLocOrInvalid(ByteLength); }
/// Returns true if the given source location is contained in the range.
bool contains(SourceLoc loc) const {
auto less = std::less<const char *>();
auto less_equal = std::less_equal<const char *>();
return less_equal(getStart().Value.getPointer(), loc.Value.getPointer()) &&
less(loc.Value.getPointer(), getEnd().Value.getPointer());
}
bool contains(CharSourceRange Other) const {
auto less_equal = std::less_equal<const char *>();
return contains(Other.getStart()) &&
less_equal(Other.getEnd().Value.getPointer(), getEnd().Value.getPointer());
}
/// expands *this to cover Other
void widen(CharSourceRange Other) {
auto Diff = Other.getEnd().Value.getPointer() - getEnd().Value.getPointer();
if (Diff > 0) {
ByteLength += Diff;
}
const auto MyStartPtr = getStart().Value.getPointer();
Diff = MyStartPtr - Other.getStart().Value.getPointer();
if (Diff > 0) {
ByteLength += Diff;
Start = SourceLoc(llvm::SMLoc::getFromPointer(MyStartPtr - Diff));
}
}
bool overlaps(CharSourceRange Other) const {
if (getByteLength() == 0 || Other.getByteLength() == 0) return false;
return contains(Other.getStart()) || Other.contains(getStart());
}
StringRef str() const {
return StringRef(Start.Value.getPointer(), ByteLength);
}
/// 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;
}
/// Print out the CharSourceRange. If the locations are in the same buffer
/// as specified by LastBufferID, then we don't print the filename. If not,
/// we do print the filename, and then update LastBufferID with the BufferID
/// printed.
void print(raw_ostream &OS, const SourceManager &SM,
unsigned &LastBufferID, bool PrintText = true) const;
void print(raw_ostream &OS, const SourceManager &SM,
bool PrintText = true) const {
unsigned Tmp = ~0U;
print(OS, SM, Tmp, PrintText);
}
SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM));
};
} // end namespace swift
namespace llvm {
template <typename T> struct DenseMapInfo;
template <> struct DenseMapInfo<swift::SourceLoc> {
static swift::SourceLoc getEmptyKey() {
return swift::SourceLoc(
SMLoc::getFromPointer(DenseMapInfo<const char *>::getEmptyKey()));
}
static swift::SourceLoc getTombstoneKey() {
// Make this different from empty key. See for context:
// http://lists.llvm.org/pipermail/llvm-dev/2015-July/088744.html
return swift::SourceLoc(
SMLoc::getFromPointer(DenseMapInfo<const char *>::getTombstoneKey()));
}
static unsigned getHashValue(const swift::SourceLoc &Val) {
return DenseMapInfo<const void *>::getHashValue(
Val.getOpaquePointerValue());
}
static bool isEqual(const swift::SourceLoc &LHS,
const swift::SourceLoc &RHS) {
return LHS == RHS;
}
};
template <> struct DenseMapInfo<swift::SourceRange> {
static swift::SourceRange getEmptyKey() {
return swift::SourceRange(swift::SourceLoc(
SMLoc::getFromPointer(DenseMapInfo<const char *>::getEmptyKey())));
}
static swift::SourceRange getTombstoneKey() {
// Make this different from empty key. See for context:
// http://lists.llvm.org/pipermail/llvm-dev/2015-July/088744.html
return swift::SourceRange(swift::SourceLoc(
SMLoc::getFromPointer(DenseMapInfo<const char *>::getTombstoneKey())));
}
static unsigned getHashValue(const swift::SourceRange &Val) {
return hash_combine(Val.Start.getOpaquePointerValue(),
Val.End.getOpaquePointerValue());
}
static bool isEqual(const swift::SourceRange &LHS,
const swift::SourceRange &RHS) {
return LHS == RHS;
}
};
} // namespace llvm
#endif // SWIFT_BASIC_SOURCELOC_H