/// able to use many of the functions on spans in codemap and you cannot assume
/// that the length of the span = hi - lo; there may be space in the BytePos
/// range between files.
-#[derive(Clone, Copy, Hash)]
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Span {
pub lo: BytePos,
pub hi: BytePos,
impl Span {
/// Returns `self` if `self` is not the dummy span, and `other` otherwise.
pub fn substitute_dummy(self, other: Span) -> Span {
- if self == DUMMY_SP { other } else { self }
+ if self.source_equal(&DUMMY_SP) { other } else { self }
}
pub fn contains(self, other: Span) -> bool {
self.lo <= other.lo && other.hi <= self.hi
}
+ /// Return true if the spans are equal with regards to the source text.
+ ///
+ /// Use this instead of `==` when either span could be generated code,
+ /// and you only care that they point to the same bytes of source text.
+ pub fn source_equal(&self, other: &Span) -> bool {
+ self.lo == other.lo && self.hi == other.hi
+ }
+
/// Returns `Some(span)`, a union of `self` and `other`, on overlap.
pub fn merge(self, other: Span) -> Option<Span> {
if self.expn_id != other.expn_id {
pub span: Span,
}
-impl PartialEq for Span {
- fn eq(&self, other: &Span) -> bool {
- return (*self).lo == (*other).lo && (*self).hi == (*other).hi;
- }
- fn ne(&self, other: &Span) -> bool { !(*self).eq(other) }
-}
-
-impl Eq for Span {}
-
impl Encodable for Span {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("Span", 2, |s| {
}
pub fn span_to_string(&self, sp: Span) -> String {
- if self.files.borrow().is_empty() && sp == DUMMY_SP {
+ if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
return "no-location".to_string();
}
/// the macro callsite that expanded to it.
pub fn source_callsite(&self, sp: Span) -> Span {
let mut span = sp;
+ // Special case - if a macro is parsed as an argument to another macro, the source
+ // callsite is the first callsite, which is also source-equivalent to the span.
+ let mut first = true;
while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN {
if let Some(callsite) = self.with_expn_info(span.expn_id,
|ei| ei.map(|ei| ei.call_site.clone())) {
+ if first && span.source_equal(&callsite) {
+ if self.lookup_char_pos(span.lo).file.is_real_file() {
+ return Span { expn_id: NO_EXPANSION, .. span };
+ }
+ }
+ first = false;
span = callsite;
}
else {
/// corresponding to the source callsite.
pub fn source_callee(&self, sp: Span) -> Option<NameAndSpan> {
let mut span = sp;
+ // Special case - if a macro is parsed as an argument to another macro, the source
+ // callsite is source-equivalent to the span, and the source callee is the first callee.
+ let mut first = true;
while let Some(callsite) = self.with_expn_info(span.expn_id,
|ei| ei.map(|ei| ei.call_site.clone())) {
+ if first && span.source_equal(&callsite) {
+ if self.lookup_char_pos(span.lo).file.is_real_file() {
+ return self.with_expn_info(span.expn_id,
+ |ei| ei.map(|ei| ei.callee.clone()));
+ }
+ }
+ first = false;
if let Some(_) = self.with_expn_info(callsite.expn_id,
- |ei| ei.map(|ei| ei.call_site.clone())) {
+ |ei| ei.map(|ei| ei.call_site.clone())) {
span = callsite;
}
else {
expninfo.map_or(/* hit the top level */ true, |info| {
let span_comes_from_this_expansion =
- info.callee.span.map_or(span == info.call_site, |mac_span| {
+ info.callee.span.map_or(span.source_equal(&info.call_site), |mac_span| {
mac_span.contains(span)
});