From 7225f668874779f9791fec778e2502e45b311e37 Mon Sep 17 00:00:00 2001
From: Rich Kadel <richkadel@google.com>
Date: Thu, 27 Aug 2020 13:58:01 -0700
Subject: [PATCH] Adds two source span utility functions used in source-based
 coverage

`span.is_empty()` - returns true if `lo()` and `hi()` are equal. This is
not only a convenience, but makes it clear that a `Span` can be empty
(that is, retrieving the source for an empty `Span` will return an empty
string), and codifies the (otherwise undocumented--in the rustc_span
package, at least) fact that `Span` is a half-open interval (where
`hi()` is the open end).

`source_map.lookup_file_span()` - returns an enclosing `Span`
representing the start and end positions of the file enclosing the given
`BytePos`. This gives developers a clear way to quickly determine if any
any other `BytePos` or `Span` is also from the same file (for example,
by simply calling `file_span.contains(span)`).

This results in much simpler code and is much more runtime efficient
compared with the obvious alternative: calling `source_map.lookup_line()`
for any two `Span`'s byte positions, handle both arms of the `Result`
(both contain the file), and then compare files. It is also more
efficient than the non-public method `lookup_source_file_idx()` for each
`BytePos`, because, while comparing the internal source file indexes
would be efficient, looking up the source file index for every `BytePos`
or `Span` to be compared requires a binary search (worst case
performance being O(log n) for every lookup).

`source_map.lookup_file_span()` performs the binary search only once, to
get the `file_span` result that can be used to compare to any number of
other `BytePos` or `Span` values and those comparisons are always O(1).
---
 compiler/rustc_span/src/lib.rs        | 7 +++++++
 compiler/rustc_span/src/source_map.rs | 9 +++++++++
 2 files changed, 16 insertions(+)

diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index c654dade2abd4..b478a1d15c506 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -400,6 +400,13 @@ impl Span {
         span.with_lo(span.hi)
     }
 
+    #[inline]
+    /// Returns true if hi == lo
+    pub fn is_empty(&self) -> bool {
+        let span = self.data();
+        span.hi == span.lo
+    }
+
     /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
     pub fn substitute_dummy(self, other: Span) -> Span {
         if self.is_dummy() { other } else { self }
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 7c656db22ed8c..37596b8ef6fca 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -487,6 +487,15 @@ impl SourceMap {
         }
     }
 
+    /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given
+    /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same
+    /// file.
+    pub fn lookup_file_span(&self, pos: BytePos) -> Span {
+        let idx = self.lookup_source_file_idx(pos);
+        let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx];
+        Span::with_root_ctxt(start_pos, end_pos)
+    }
+
     /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If
     /// there are gaps between LHS and RHS, the resulting union will cross these gaps.
     /// For this to work,