//===--- 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/Hashing.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/SMLoc.h" #include #include 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(); } /// An explicit bool operator so one can check if a SourceLoc is valid in an /// if statement: /// /// if (auto x = getSourceLoc()) { ... } explicit operator bool() 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(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(); } /// An explicit bool operator so one can check if a SourceRange is valid in an /// if statement: /// /// if (auto x = getSourceRange()) { ... } explicit operator bool() const { return isValid(); } /// Combine the given source ranges into the smallest contiguous SourceRange /// that includes them all, ignoring any invalid ranges present. static SourceRange combine(ArrayRef ranges); /// Combine the given source ranges into the smallest contiguous SourceRange /// that includes them all, ignoring any invalid ranges present. template static SourceRange combine(T... ranges) { return SourceRange::combine({ranges...}); } /// Extend this SourceRange to the smallest continuous SourceRange that /// includes both this range and the other one. void widen(SourceRange Other); /// Checks whether this range contains the given location. Note that the given /// location should correspond to the start of a token, since locations inside /// the last token may be considered outside the range by this function. bool contains(SourceLoc Loc) const; /// Checks whether this range overlaps with the given range. bool overlaps(SourceRange Other) const; 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(); auto less_equal = std::less_equal(); 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(); 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 struct DenseMapInfo; template <> struct DenseMapInfo { static swift::SourceLoc getEmptyKey() { return swift::SourceLoc( SMLoc::getFromPointer(DenseMapInfo::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::getTombstoneKey())); } static unsigned getHashValue(const swift::SourceLoc &Val) { return DenseMapInfo::getHashValue( Val.getOpaquePointerValue()); } static bool isEqual(const swift::SourceLoc &LHS, const swift::SourceLoc &RHS) { return LHS == RHS; } }; template <> struct DenseMapInfo { static swift::SourceRange getEmptyKey() { return swift::SourceRange(swift::SourceLoc( SMLoc::getFromPointer(DenseMapInfo::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::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