about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-08-11 03:00:05 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-08-15 20:39:27 +0300
commit73dee258c19a6e9e8249a0d7ff1db54014d0c7a1 (patch)
treec8d742151c2b460235059195250a8fb27ef170d0 /src
parent6cb28b6617e25b74389f1cee2ec0335c2ccfb865 (diff)
downloadrust-73dee258c19a6e9e8249a0d7ff1db54014d0c7a1.tar.gz
rust-73dee258c19a6e9e8249a0d7ff1db54014d0c7a1.zip
hygiene: Remove `Option`s from functions returning `ExpnInfo`
The expansion info is not optional and should always exist
Diffstat (limited to 'src')
-rw-r--r--src/librustc/lint/internal.rs33
-rw-r--r--src/librustc/lint/mod.rs19
-rw-r--r--src/librustc/traits/error_reporting.rs8
-rw-r--r--src/librustc/ty/query/on_disk_cache.rs18
-rw-r--r--src/librustc_codegen_ssa/back/write.rs5
-rw-r--r--src/librustc_lint/unused.rs5
-rw-r--r--src/librustc_resolve/macros.rs3
-rw-r--r--src/libsyntax/ext/base.rs21
-rw-r--r--src/libsyntax/ext/expand.rs2
-rw-r--r--src/libsyntax/ext/proc_macro_server.rs4
-rw-r--r--src/libsyntax/parse/parser.rs1
-rw-r--r--src/libsyntax/source_map.rs13
-rw-r--r--src/libsyntax_pos/hygiene.rs58
-rw-r--r--src/libsyntax_pos/lib.rs63
14 files changed, 98 insertions, 155 deletions
diff --git a/src/librustc/lint/internal.rs b/src/librustc/lint/internal.rs
index d9ad34a5297..29106fe000b 100644
--- a/src/librustc/lint/internal.rs
+++ b/src/librustc/lint/internal.rs
@@ -9,7 +9,6 @@ use errors::Applicability;
 use rustc_data_structures::fx::FxHashMap;
 use syntax::ast::{Ident, Item, ItemKind};
 use syntax::symbol::{sym, Symbol};
-use syntax_pos::ExpnInfo;
 
 declare_tool_lint! {
     pub rustc::DEFAULT_HASH_TYPES,
@@ -228,30 +227,20 @@ impl EarlyLintPass for LintPassImpl {
         if let ItemKind::Impl(_, _, _, _, Some(lint_pass), _, _) = &item.node {
             if let Some(last) = lint_pass.path.segments.last() {
                 if last.ident.name == sym::LintPass {
-                    match &lint_pass.path.span.ctxt().outer_expn_info() {
-                        Some(info) if is_lint_pass_expansion(info) => {}
-                        _ => {
-                            cx.struct_span_lint(
-                                LINT_PASS_IMPL_WITHOUT_MACRO,
-                                lint_pass.path.span,
-                                "implementing `LintPass` by hand",
-                            )
-                            .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
-                            .emit();
-                        }
+                    let expn_info = lint_pass.path.span.ctxt().outer_expn_info();
+                    let call_site = expn_info.call_site;
+                    if expn_info.kind.descr() != sym::impl_lint_pass &&
+                       call_site.ctxt().outer_expn_info().kind.descr() != sym::declare_lint_pass {
+                        cx.struct_span_lint(
+                            LINT_PASS_IMPL_WITHOUT_MACRO,
+                            lint_pass.path.span,
+                            "implementing `LintPass` by hand",
+                        )
+                        .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
+                        .emit();
                     }
                 }
             }
         }
     }
 }
-
-fn is_lint_pass_expansion(expn_info: &ExpnInfo) -> bool {
-    if expn_info.kind.descr() == sym::impl_lint_pass {
-        true
-    } else if let Some(info) = expn_info.call_site.ctxt().outer_expn_info() {
-        info.kind.descr() == sym::declare_lint_pass
-    } else {
-        false
-    }
-}
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 8cb5b1e26d9..3729ee81f5c 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -885,21 +885,16 @@ pub fn provide(providers: &mut Providers<'_>) {
 /// This is used to test whether a lint should not even begin to figure out whether it should
 /// be reported on the current node.
 pub fn in_external_macro(sess: &Session, span: Span) -> bool {
-    let info = match span.ctxt().outer_expn_info() {
-        Some(info) => info,
-        // no ExpnInfo means this span doesn't come from a macro
-        None => return false,
-    };
-
-    match info.kind {
+    let expn_info = span.ctxt().outer_expn_info();
+    match expn_info.kind {
         ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
         ExpnKind::Desugaring(_) => true, // well, it's "external"
         ExpnKind::Macro(MacroKind::Bang, _) => {
-            if info.def_site.is_dummy() {
+            if expn_info.def_site.is_dummy() {
                 // dummy span for the def_site means it's an external macro
                 return true;
             }
-            match sess.source_map().span_to_snippet(info.def_site) {
+            match sess.source_map().span_to_snippet(expn_info.def_site) {
                 Ok(code) => !code.starts_with("macro_rules"),
                 // no snippet = external macro or compiler-builtin expansion
                 Err(_) => true,
@@ -911,10 +906,8 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
 
 /// Returns whether `span` originates in a derive macro's expansion
 pub fn in_derive_expansion(span: Span) -> bool {
-    if let Some(info) = span.ctxt().outer_expn_info() {
-        if let ExpnKind::Macro(MacroKind::Derive, _) = info.kind {
-            return true;
-        }
+    if let ExpnKind::Macro(MacroKind::Derive, _) = span.ctxt().outer_expn_info().kind {
+        return true;
     }
     false
 }
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 83bd5c56040..20568d4709b 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -36,7 +36,7 @@ use errors::{Applicability, DiagnosticBuilder};
 use std::fmt;
 use syntax::ast;
 use syntax::symbol::sym;
-use syntax_pos::{DUMMY_SP, Span, ExpnInfo, ExpnKind};
+use syntax_pos::{DUMMY_SP, Span, ExpnKind};
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn report_fulfillment_errors(&self,
@@ -61,9 +61,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             // We want to ignore desugarings here: spans are equivalent even
             // if one is the result of a desugaring and the other is not.
             let mut span = error.obligation.cause.span;
-            if let Some(ExpnInfo { kind: ExpnKind::Desugaring(_), def_site, .. })
-                    = span.ctxt().outer_expn_info() {
-                span = def_site;
+            let expn_info = span.ctxt().outer_expn_info();
+            if let ExpnKind::Desugaring(_) = expn_info.kind {
+                span = expn_info.call_site;
             }
 
             error_map.entry(span).or_default().push(
diff --git a/src/librustc/ty/query/on_disk_cache.rs b/src/librustc/ty/query/on_disk_cache.rs
index 1c5baa638c2..2286271b9eb 100644
--- a/src/librustc/ty/query/on_disk_cache.rs
+++ b/src/librustc/ty/query/on_disk_cache.rs
@@ -820,18 +820,14 @@ where
             TAG_NO_EXPANSION_INFO.encode(self)
         } else {
             let (expn_id, expn_info) = span_data.ctxt.outer_expn_with_info();
-            if let Some(expn_info) = expn_info {
-                if let Some(pos) = self.expn_info_shorthands.get(&expn_id).cloned() {
-                    TAG_EXPANSION_INFO_SHORTHAND.encode(self)?;
-                    pos.encode(self)
-                } else {
-                    TAG_EXPANSION_INFO_INLINE.encode(self)?;
-                    let pos = AbsoluteBytePos::new(self.position());
-                    self.expn_info_shorthands.insert(expn_id, pos);
-                    expn_info.encode(self)
-                }
+            if let Some(pos) = self.expn_info_shorthands.get(&expn_id).cloned() {
+                TAG_EXPANSION_INFO_SHORTHAND.encode(self)?;
+                pos.encode(self)
             } else {
-                TAG_NO_EXPANSION_INFO.encode(self)
+                TAG_EXPANSION_INFO_INLINE.encode(self)?;
+                let pos = AbsoluteBytePos::new(self.position());
+                self.expn_info_shorthands.insert(expn_id, pos);
+                expn_info.encode(self)
             }
         }
     }
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index c9e4663fdbd..240264a9822 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -1775,10 +1775,7 @@ impl SharedEmitterMain {
                     }
                 }
                 Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => {
-                    match ExpnId::from_u32(cookie).expn_info() {
-                        Some(ei) => sess.span_err(ei.call_site, &msg),
-                        None     => sess.err(&msg),
-                    }
+                    sess.span_err(ExpnId::from_u32(cookie).expn_info().call_site, &msg)
                 }
                 Ok(SharedEmitterMessage::AbortIfErrors) => {
                     sess.abort_if_errors();
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 9cad8f58d41..1bb05bda69f 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -517,9 +517,8 @@ impl EarlyLintPass for UnusedParens {
                 // trigger in situations that macro authors shouldn't have to care about, e.g.,
                 // when a parenthesized token tree matched in one macro expansion is matched as
                 // an expression in another and used as a fn/method argument (Issue #47775)
-                if e.span.ctxt().outer_expn_info()
-                    .map_or(false, |info| info.call_site.from_expansion()) {
-                        return;
+                if e.span.ctxt().outer_expn_info().call_site.from_expansion() {
+                    return;
                 }
                 let msg = format!("{} argument", call_kind);
                 for arg in args_to_check {
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 71e26dac57c..97b0f825ee9 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -346,8 +346,7 @@ impl<'a> Resolver<'a> {
 
         // Possibly apply the macro helper hack
         if kind == Some(MacroKind::Bang) && path.len() == 1 &&
-           path[0].ident.span.ctxt().outer_expn_info()
-               .map_or(false, |info| info.local_inner_macros) {
+           path[0].ident.span.ctxt().outer_expn_info().local_inner_macros {
             let root = Ident::new(kw::DollarCrate, path[0].ident.span);
             path.insert(0, Segment::from_ident(root));
         }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index fd6b9138fde..8eacb96e3ff 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -756,10 +756,7 @@ impl<'a> ExtCtxt<'a> {
     pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }
     pub fn cfg(&self) -> &ast::CrateConfig { &self.parse_sess.config }
     pub fn call_site(&self) -> Span {
-        match self.current_expansion.id.expn_info() {
-            Some(expn_info) => expn_info.call_site,
-            None => DUMMY_SP,
-        }
+        self.current_expansion.id.expn_info().call_site
     }
     pub fn backtrace(&self) -> SyntaxContext {
         SyntaxContext::root().apply_mark(self.current_expansion.id)
@@ -772,17 +769,13 @@ impl<'a> ExtCtxt<'a> {
         let mut ctxt = self.backtrace();
         let mut last_macro = None;
         loop {
-            if ctxt.outer_expn_info().map_or(None, |info| {
-                if info.kind.descr() == sym::include {
-                    // Stop going up the backtrace once include! is encountered
-                    return None;
-                }
-                ctxt = info.call_site.ctxt();
-                last_macro = Some(info.call_site);
-                Some(())
-            }).is_none() {
-                break
+            let expn_info = ctxt.outer_expn_info();
+            // Stop going up the backtrace once include! is encountered
+            if expn_info.is_root() || expn_info.kind.descr() == sym::include {
+                break;
             }
+            ctxt = expn_info.call_site.ctxt();
+            last_macro = Some(expn_info.call_site);
         }
         last_macro
     }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 5f4074a217a..6f3e8f14b0b 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -475,7 +475,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
 
         if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
-            let info = self.cx.current_expansion.id.expn_info().unwrap();
+            let info = self.cx.current_expansion.id.expn_info();
             let suggested_limit = self.cx.ecfg.recursion_limit * 2;
             let mut err = self.cx.struct_span_err(info.call_site,
                 &format!("recursion limit reached while expanding the macro `{}`",
diff --git a/src/libsyntax/ext/proc_macro_server.rs b/src/libsyntax/ext/proc_macro_server.rs
index fd93910004e..d370431a5da 100644
--- a/src/libsyntax/ext/proc_macro_server.rs
+++ b/src/libsyntax/ext/proc_macro_server.rs
@@ -362,7 +362,7 @@ pub(crate) struct Rustc<'a> {
 impl<'a> Rustc<'a> {
     pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
         // No way to determine def location for a proc macro right now, so use call location.
-        let location = cx.current_expansion.id.expn_info().unwrap().call_site;
+        let location = cx.current_expansion.id.expn_info().call_site;
         let to_span = |transparency| {
             location.with_ctxt(
                 SyntaxContext::root()
@@ -677,7 +677,7 @@ impl server::Span for Rustc<'_> {
         self.sess.source_map().lookup_char_pos(span.lo()).file
     }
     fn parent(&mut self, span: Self::Span) -> Option<Self::Span> {
-        span.ctxt().outer_expn_info().map(|i| i.call_site)
+        span.parent()
     }
     fn source(&mut self, span: Self::Span) -> Self::Span {
         span.source_callsite()
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 3b0af88f651..89725d8b339 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -13,7 +13,6 @@ mod generics;
 use crate::ast::{self, AttrStyle, Attribute, Arg, BindingMode, StrStyle, SelfKind};
 use crate::ast::{FnDecl, Ident, IsAsync, MacDelimiter, Mutability, TyKind};
 use crate::ast::{Visibility, VisibilityKind, Unsafety, CrateSugar};
-use crate::ext::hygiene::SyntaxContext;
 use crate::source_map::{self, respan};
 use crate::parse::{SeqSep, literal, token};
 use crate::parse::lexer::UnmatchedBrace;
diff --git a/src/libsyntax/source_map.rs b/src/libsyntax/source_map.rs
index 74cab00d3c1..da7eb6add41 100644
--- a/src/libsyntax/source_map.rs
+++ b/src/libsyntax/source_map.rs
@@ -31,12 +31,13 @@ mod tests;
 /// otherwise return the call site span up to the `enclosing_sp` by
 /// following the `expn_info` chain.
 pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
-    let call_site1 = sp.ctxt().outer_expn_info().map(|ei| ei.call_site);
-    let call_site2 = enclosing_sp.ctxt().outer_expn_info().map(|ei| ei.call_site);
-    match (call_site1, call_site2) {
-        (None, _) => sp,
-        (Some(call_site1), Some(call_site2)) if call_site1 == call_site2 => sp,
-        (Some(call_site1), _) => original_sp(call_site1, enclosing_sp),
+    let expn_info1 = sp.ctxt().outer_expn_info();
+    let expn_info2 = enclosing_sp.ctxt().outer_expn_info();
+    if expn_info1.is_root() ||
+       !expn_info2.is_root() && expn_info1.call_site == expn_info2.call_site {
+        sp
+    } else {
+        original_sp(expn_info1.call_site, enclosing_sp)
     }
 }
 
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index c832e058cdf..743bd437ee5 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -112,8 +112,8 @@ impl ExpnId {
     }
 
     #[inline]
-    pub fn expn_info(self) -> Option<ExpnInfo> {
-        HygieneData::with(|data| data.expn_info(self).cloned())
+    pub fn expn_info(self) -> ExpnInfo {
+        HygieneData::with(|data| data.expn_info(self).clone())
     }
 
     #[inline]
@@ -139,12 +139,9 @@ impl ExpnId {
     #[inline]
     pub fn looks_like_proc_macro_derive(self) -> bool {
         HygieneData::with(|data| {
-            if data.default_transparency(self) == Transparency::Opaque {
-                if let Some(expn_info) = data.expn_info(self) {
-                    if let ExpnKind::Macro(MacroKind::Derive, _) = expn_info.kind {
-                        return true;
-                    }
-                }
+            let expn_info = data.expn_info(self);
+            if let ExpnKind::Macro(MacroKind::Derive, _) = expn_info.kind {
+                return expn_info.default_transparency == Transparency::Opaque;
             }
             false
         })
@@ -190,16 +187,9 @@ impl HygieneData {
         self.expn_data[expn_id.0 as usize].parent
     }
 
-    fn expn_info(&self, expn_id: ExpnId) -> Option<&ExpnInfo> {
-        if expn_id != ExpnId::root() {
-            Some(self.expn_data[expn_id.0 as usize].expn_info.as_ref()
-                     .expect("no expansion info for an expansion ID"))
-        } else {
-            // FIXME: Some code relies on `expn_info().is_none()` meaning "no expansion".
-            // Introduce a method for checking for "no expansion" instead and always return
-            // `ExpnInfo` from this function instead of the `Option`.
-            None
-        }
+    fn expn_info(&self, expn_id: ExpnId) -> &ExpnInfo {
+        self.expn_data[expn_id.0 as usize].expn_info.as_ref()
+            .expect("no expansion info for an expansion ID")
     }
 
     fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool {
@@ -212,12 +202,6 @@ impl HygieneData {
         true
     }
 
-    fn default_transparency(&self, expn_id: ExpnId) -> Transparency {
-        self.expn_info(expn_id).map_or(
-            Transparency::SemiTransparent, |einfo| einfo.default_transparency
-        )
-    }
-
     fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext {
         self.syntax_context_data[ctxt.0 as usize].opaque
     }
@@ -256,11 +240,7 @@ impl HygieneData {
 
     fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
         while span.from_expansion() && span.ctxt() != to {
-            if let Some(info) = self.expn_info(self.outer_expn(span.ctxt())) {
-                span = info.call_site;
-            } else {
-                break;
-            }
+            span = self.expn_info(self.outer_expn(span.ctxt())).call_site;
         }
         span
     }
@@ -275,7 +255,9 @@ impl HygieneData {
 
     fn apply_mark(&mut self, ctxt: SyntaxContext, expn_id: ExpnId) -> SyntaxContext {
         assert_ne!(expn_id, ExpnId::root());
-        self.apply_mark_with_transparency(ctxt, expn_id, self.default_transparency(expn_id))
+        self.apply_mark_with_transparency(
+            ctxt, expn_id, self.expn_info(expn_id).default_transparency
+        )
     }
 
     fn apply_mark_with_transparency(&mut self, ctxt: SyntaxContext, expn_id: ExpnId,
@@ -285,8 +267,7 @@ impl HygieneData {
             return self.apply_mark_internal(ctxt, expn_id, transparency);
         }
 
-        let call_site_ctxt =
-            self.expn_info(expn_id).map_or(SyntaxContext::root(), |info| info.call_site.ctxt());
+        let call_site_ctxt = self.expn_info(expn_id).call_site.ctxt();
         let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
             self.modern(call_site_ctxt)
         } else {
@@ -581,17 +562,17 @@ impl SyntaxContext {
     /// `ctxt.outer_expn_info()` is equivalent to but faster than
     /// `ctxt.outer_expn().expn_info()`.
     #[inline]
-    pub fn outer_expn_info(self) -> Option<ExpnInfo> {
-        HygieneData::with(|data| data.expn_info(data.outer_expn(self)).cloned())
+    pub fn outer_expn_info(self) -> ExpnInfo {
+        HygieneData::with(|data| data.expn_info(data.outer_expn(self)).clone())
     }
 
     /// `ctxt.outer_expn_with_info()` is equivalent to but faster than
     /// `{ let outer = ctxt.outer_expn(); (outer, outer.expn_info()) }`.
     #[inline]
-    pub fn outer_expn_with_info(self) -> (ExpnId, Option<ExpnInfo>) {
+    pub fn outer_expn_with_info(self) -> (ExpnId, ExpnInfo) {
         HygieneData::with(|data| {
             let outer = data.outer_expn(self);
-            (outer, data.expn_info(outer).cloned())
+            (outer, data.expn_info(outer).clone())
         })
     }
 
@@ -681,6 +662,11 @@ impl ExpnInfo {
             ..ExpnInfo::default(kind, call_site, edition)
         }
     }
+
+    #[inline]
+    pub fn is_root(&self) -> bool {
+        if let ExpnKind::Root = self.kind { true } else { false }
+    }
 }
 
 /// Expansion kind.
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 7c8539198b9..7af426eaa13 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -355,20 +355,20 @@ impl Span {
     /// Returns the source span -- this is either the supplied span, or the span for
     /// the macro callsite that expanded to it.
     pub fn source_callsite(self) -> Span {
-        self.ctxt().outer_expn_info().map(|info| info.call_site.source_callsite()).unwrap_or(self)
+        let expn_info = self.ctxt().outer_expn_info();
+        if !expn_info.is_root() { expn_info.call_site.source_callsite() } else { self }
     }
 
     /// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
     /// if any.
     pub fn parent(self) -> Option<Span> {
-        self.ctxt().outer_expn_info().map(|i| i.call_site)
+        let expn_info = self.ctxt().outer_expn_info();
+        if !expn_info.is_root() { Some(expn_info.call_site) } else { None }
     }
 
     /// Edition of the crate from which this span came.
     pub fn edition(self) -> edition::Edition {
-        self.ctxt().outer_expn_info().map_or_else(|| {
-            Edition::from_session()
-        }, |einfo| einfo.edition)
+        self.ctxt().outer_expn_info().edition
     }
 
     #[inline]
@@ -387,49 +387,39 @@ impl Span {
     /// else returns the `ExpnInfo` for the macro definition
     /// corresponding to the source callsite.
     pub fn source_callee(self) -> Option<ExpnInfo> {
-        fn source_callee(info: ExpnInfo) -> ExpnInfo {
-            match info.call_site.ctxt().outer_expn_info() {
-                Some(info) => source_callee(info),
-                None => info,
-            }
+        fn source_callee(expn_info: ExpnInfo) -> ExpnInfo {
+            let next_expn_info = expn_info.call_site.ctxt().outer_expn_info();
+            if !next_expn_info.is_root() { source_callee(next_expn_info) } else { expn_info }
         }
-        self.ctxt().outer_expn_info().map(source_callee)
+        let expn_info = self.ctxt().outer_expn_info();
+        if !expn_info.is_root() { Some(source_callee(expn_info)) } else { None }
     }
 
     /// Checks if a span is "internal" to a macro in which `#[unstable]`
     /// items can be used (that is, a macro marked with
     /// `#[allow_internal_unstable]`).
     pub fn allows_unstable(&self, feature: Symbol) -> bool {
-        match self.ctxt().outer_expn_info() {
-            Some(info) => info
-                .allow_internal_unstable
-                .map_or(false, |features| features.iter().any(|&f|
-                    f == feature || f == sym::allow_internal_unstable_backcompat_hack
-                )),
-            None => false,
-        }
+        self.ctxt().outer_expn_info().allow_internal_unstable.map_or(false, |features| {
+            features.iter().any(|&f| {
+                f == feature || f == sym::allow_internal_unstable_backcompat_hack
+            })
+        })
     }
 
     /// Checks if this span arises from a compiler desugaring of kind `kind`.
     pub fn is_desugaring(&self, kind: DesugaringKind) -> bool {
-        match self.ctxt().outer_expn_info() {
-            Some(info) => match info.kind {
-                ExpnKind::Desugaring(k) => k == kind,
-                _ => false,
-            },
-            None => false,
+        match self.ctxt().outer_expn_info().kind {
+            ExpnKind::Desugaring(k) => k == kind,
+            _ => false,
         }
     }
 
     /// Returns the compiler desugaring that created this span, or `None`
     /// if this span is not from a desugaring.
     pub fn desugaring_kind(&self) -> Option<DesugaringKind> {
-        match self.ctxt().outer_expn_info() {
-            Some(info) => match info.kind {
-                ExpnKind::Desugaring(k) => Some(k),
-                _ => None
-            },
-            None => None
+        match self.ctxt().outer_expn_info().kind {
+            ExpnKind::Desugaring(k) => Some(k),
+            _ => None
         }
     }
 
@@ -437,16 +427,17 @@ impl Span {
     /// can be used without triggering the `unsafe_code` lint
     //  (that is, a macro marked with `#[allow_internal_unsafe]`).
     pub fn allows_unsafe(&self) -> bool {
-        match self.ctxt().outer_expn_info() {
-            Some(info) => info.allow_internal_unsafe,
-            None => false,
-        }
+        self.ctxt().outer_expn_info().allow_internal_unsafe
     }
 
     pub fn macro_backtrace(mut self) -> Vec<MacroBacktrace> {
         let mut prev_span = DUMMY_SP;
         let mut result = vec![];
-        while let Some(info) = self.ctxt().outer_expn_info() {
+        loop {
+            let info = self.ctxt().outer_expn_info();
+            if info.is_root() {
+                break;
+            }
             // Don't print recursive invocations.
             if !info.call_site.source_equal(&prev_span) {
                 let (pre, post) = match info.kind {