Upgrade rust/crates/proc-macro-error to 1.0.4 am: eef7c23c35 am: 7c5e37eb1c am: 632a02a563 am: 10abb6c99c

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/proc-macro-error/+/1414396

Change-Id: Ibc512b5f4c6cdcb9e2c325a2a9f3311ce05c1513
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 0b126fe..1561cb4 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "69743a4ceab6c8da1ddb473f54a6a01840de90d9"
+    "sha1": "e231741c47af1beda78d53aee29500cccb8266cd"
   }
 }
diff --git a/Android.bp b/Android.bp
index 583b93d..da13ffc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5,6 +5,11 @@
     crate_name: "proc_macro_error",
     srcs: ["src/lib.rs"],
     edition: "2018",
+    features: [
+        "default",
+        "syn",
+        "syn-error",
+    ],
     flags: [
         "--cfg skip_ui_tests",
     ],
@@ -20,10 +25,9 @@
 }
 
 // dependent_library ["feature_list"]
-//   proc-macro-error-attr-1.0.2
-//   proc-macro2-1.0.19 "default,proc-macro"
+//   proc-macro-error-attr-1.0.4
+//   proc-macro2-1.0.23 "default,proc-macro"
 //   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.36 "clone-impls,default,derive,parsing,printing,proc-macro,quote,visit"
-//   syn-mid-0.5.0
+//   syn-1.0.42 "clone-impls,default,derive,parsing,printing,proc-macro,quote,visit"
 //   unicode-xid-0.2.1 "default"
 //   version_check-0.9.2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1a2d3c..3c422f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+# v1.0.4 (2020-7-31)
+
+* `SpanRange` facility is now public.
+* Docs have been improved.
+* Introduced the `syn-error` feature so you can opt-out from the `syn` dependency.
+
+# v1.0.3 (2020-6-26)
+
+* Corrected a few typos.
+* Fixed the `emit_call_site_warning` macro.
+
 # v1.0.2 (2020-4-9)
 
 * An obsolete note was removed from documentation.
diff --git a/Cargo.toml b/Cargo.toml
index fc90d7d..869585f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "proc-macro-error"
-version = "1.0.2"
+version = "1.0.4"
 authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"]
 build = "build.rs"
 description = "Almost drop-in replacement to panics in proc-macros"
@@ -25,7 +25,7 @@
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
 [dependencies.proc-macro-error-attr]
-version = "=1.0.2"
+version = "=1.0.4"
 
 [dependencies.proc-macro2]
 version = "1"
@@ -35,8 +35,11 @@
 
 [dependencies.syn]
 version = "1"
-features = ["derive", "parsing", "proc-macro", "printing"]
+optional = true
 default-features = false
+[dev-dependencies.serde_derive]
+version = "=1.0.107"
+
 [dev-dependencies.toml]
 version = "=0.5.2"
 
@@ -45,5 +48,9 @@
 features = ["diff"]
 [build-dependencies.version_check]
 version = "0.9"
+
+[features]
+default = ["syn-error"]
+syn-error = ["syn"]
 [badges.maintenance]
 status = "passively-maintained"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 83f6781..5ad358d 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "proc-macro-error"
-version = "1.0.2"
+version = "1.0.4"
 authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"]
 description = "Almost drop-in replacement to panics in proc-macros"
 
@@ -22,14 +22,23 @@
 [dependencies]
 quote = "1"
 proc-macro2 = "1"
-syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro", "printing"] }
-proc-macro-error-attr = { path = "./proc-macro-error-attr", version = "=1.0.2"}
+proc-macro-error-attr = { path = "./proc-macro-error-attr", version = "=1.0.4"}
+
+[dependencies.syn]
+version = "1"
+optional = true
+default-features = false
 
 [dev-dependencies]
 test-crate = { path = "./test-crate" }
 proc-macro-hack-test = { path = "./test-crate/proc-macro-hack-test" }
 trybuild = { version = "1.0.19", features = ["diff"] }
 toml = "=0.5.2" # DO NOT BUMP
+serde_derive = "=1.0.107" # DO NOT BUMP
 
 [build-dependencies]
 version_check = "0.9"
+
+[features]
+default = ["syn-error"]
+syn-error = ["syn"]
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index 52ba334..cc17374 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -186,7 +186,7 @@
    same "printed page" as the copyright notice for easier

    identification within third-party archives.

 

-Copyright 2019 CreepySkeleton <creepy-skeleton@yandex.ru>

+Copyright 2019-2020 CreepySkeleton <creepy-skeleton@yandex.ru>

 

 Licensed under the Apache License, Version 2.0 (the "License");

 you may not use this file except in compliance with the License.

diff --git a/LICENSE-MIT b/LICENSE-MIT
index d01f775..fc73e59 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2019 CreepySkeleton
+Copyright (c) 2019-2020 CreepySkeleton
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/METADATA b/METADATA
index 07686e8..e10f8c2 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/proc-macro-error/proc-macro-error-1.0.2.crate"
+    value: "https://static.crates.io/crates/proc-macro-error/proc-macro-error-1.0.4.crate"
   }
-  version: "1.0.2"
+  version: "1.0.4"
   license_type: NOTICE
   last_upgrade_date {
     year: 2020
-    month: 5
-    day: 6
+    month: 9
+    day: 29
   }
 }
diff --git a/README.md b/README.md
index fb1da50..7fbe07c 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,15 @@
 This crate aims to make error reporting in proc-macros simple and easy to use.
 Migrate from `panic!`-based errors for as little effort as possible!
 
-Also, there's ability to [append a dummy token stream][crate::dummy] to your errors.
+Also, you can explicitly [append a dummy token stream][crate::dummy] to your errors.
+
+To achieve his, this crate serves as a tiny shim around `proc_macro::Diagnostic` and
+`compile_error!`. It detects the most preferable way to emit errors based on compiler's version.
+When the underlying diagnostic type is finally stabilized, this crate will be simply
+delegating to it, requiring no changes in your code!
+
+So you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality
+available on stable ahead of time and your error-reporting code future-proof.
 
 ```toml
 [dependencies]
@@ -242,9 +250,9 @@
 [compl_err]: https://doc.rust-lang.org/std/macro.compile_error.html
 [`proc_macro::Diagnostic`]: https://doc.rust-lang.org/proc_macro/struct.Diagnostic.html
 
-[crate::dummy]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/dummy/index.html
-[crate::multi]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/multi/index.html
+[crate::dummy]: https://docs.rs/proc-macro-error/1/proc_macro_error/dummy/index.html
+[crate::multi]: https://docs.rs/proc-macro-error/1/proc_macro_error/multi/index.html
 
-[`abort_call_site!`]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/macro.abort_call_site.html
-[`abort!`]: https://docs.rs/proc-macro-error/0.4/proc_macro_error/macro.abort.html
+[`abort_call_site!`]: https://docs.rs/proc-macro-error/1/proc_macro_error/macro.abort_call_site.html
+[`abort!`]: https://docs.rs/proc-macro-error/1/proc_macro_error/macro.abort.html
 [guide]: https://docs.rs/proc-macro-error
diff --git a/src/diagnostic.rs b/src/diagnostic.rs
index 5ec8ce2..983e617 100644
--- a/src/diagnostic.rs
+++ b/src/diagnostic.rs
@@ -1,4 +1,4 @@
-use crate::{abort_now, check_correctness};
+use crate::{abort_now, check_correctness, sealed::Sealed, SpanRange};
 use proc_macro2::Span;
 use proc_macro2::TokenStream;
 
@@ -21,11 +21,78 @@
 #[derive(Debug)]
 pub struct Diagnostic {
     pub(crate) level: Level,
-    pub(crate) start: Span,
-    pub(crate) end: Span,
+    pub(crate) span_range: SpanRange,
     pub(crate) msg: String,
-    pub(crate) suggestions: Vec<(SuggestionKind, String, Option<Span>)>,
-    pub(crate) children: Vec<(Span, Span, String)>,
+    pub(crate) suggestions: Vec<(SuggestionKind, String, Option<SpanRange>)>,
+    pub(crate) children: Vec<(SpanRange, String)>,
+}
+
+/// A collection of methods that do not exist in `proc_macro::Diagnostic`
+/// but still useful to have around.
+///
+/// This trait is sealed and cannot be implemented outside of `proc_macro_error`.
+pub trait DiagnosticExt: Sealed {
+    /// Create a new diagnostic message that points to the `span_range`.
+    ///
+    /// This function is the same as `Diagnostic::spanned` but produces considerably
+    /// better error messages for multi-token spans on stable.
+    fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self;
+
+    /// Add another error message to self such that it will be emitted right after
+    /// the main message.
+    ///
+    /// This function is the same as `Diagnostic::span_error` but produces considerably
+    /// better error messages for multi-token spans on stable.
+    fn span_range_error(self, span_range: SpanRange, msg: String) -> Self;
+
+    /// Attach a "help" note to your main message, the note will have it's own span on nightly.
+    ///
+    /// This function is the same as `Diagnostic::span_help` but produces considerably
+    /// better error messages for multi-token spans on stable.
+    ///
+    /// # Span
+    ///
+    /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
+    fn span_range_help(self, span_range: SpanRange, msg: String) -> Self;
+
+    /// Attach a note to your main message, the note will have it's own span on nightly.
+    ///
+    /// This function is the same as `Diagnostic::span_note` but produces considerably
+    /// better error messages for multi-token spans on stable.
+    ///
+    /// # Span
+    ///
+    /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
+    fn span_range_note(self, span_range: SpanRange, msg: String) -> Self;
+}
+
+impl DiagnosticExt for Diagnostic {
+    fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self {
+        Diagnostic {
+            level,
+            span_range,
+            msg: message,
+            suggestions: vec![],
+            children: vec![],
+        }
+    }
+
+    fn span_range_error(mut self, span_range: SpanRange, msg: String) -> Self {
+        self.children.push((span_range, msg));
+        self
+    }
+
+    fn span_range_help(mut self, span_range: SpanRange, msg: String) -> Self {
+        self.suggestions
+            .push((SuggestionKind::Help, msg, Some(span_range)));
+        self
+    }
+
+    fn span_range_note(mut self, span_range: SpanRange, msg: String) -> Self {
+        self.suggestions
+            .push((SuggestionKind::Note, msg, Some(span_range)));
+        self
+    }
 }
 
 impl Diagnostic {
@@ -36,13 +103,26 @@
 
     /// Create a new diagnostic message that points to the `span`
     pub fn spanned(span: Span, level: Level, message: String) -> Self {
-        Diagnostic::double_spanned(span, span, level, message)
+        Diagnostic::spanned_range(
+            SpanRange {
+                first: span,
+                last: span,
+            },
+            level,
+            message,
+        )
     }
 
     /// Add another error message to self such that it will be emitted right after
     /// the main message.
     pub fn span_error(self, span: Span, msg: String) -> Self {
-        self.double_span_error(span, span, msg)
+        self.span_range_error(
+            SpanRange {
+                first: span,
+                last: span,
+            },
+            msg,
+        )
     }
 
     /// Attach a "help" note to your main message, the note will have it's own span on nightly.
@@ -50,10 +130,14 @@
     /// # Span
     ///
     /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
-    pub fn span_help(mut self, span: Span, msg: String) -> Self {
-        self.suggestions
-            .push((SuggestionKind::Help, msg, Some(span)));
-        self
+    pub fn span_help(self, span: Span, msg: String) -> Self {
+        self.span_range_help(
+            SpanRange {
+                first: span,
+                last: span,
+            },
+            msg,
+        )
     }
 
     /// Attach a "help" note to your main message.
@@ -67,10 +151,14 @@
     /// # Span
     ///
     /// The span is ignored on stable, the note effectively inherits its parent's (main message) span
-    pub fn span_note(mut self, span: Span, msg: String) -> Self {
-        self.suggestions
-            .push((SuggestionKind::Note, msg, Some(span)));
-        self
+    pub fn span_note(self, span: Span, msg: String) -> Self {
+        self.span_range_note(
+            SpanRange {
+                first: span,
+                last: span,
+            },
+            msg,
+        )
     }
 
     /// Attach a note to your main message
@@ -88,7 +176,7 @@
     ///
     /// # Warnings
     ///
-    /// Warnings do not get emitted on stable/beta but this function will abort anyway.
+    /// Warnings are not emitted on stable and beta, but this function will abort anyway.
     pub fn abort(self) -> ! {
         self.emit();
         abort_now()
@@ -108,22 +196,6 @@
 /// **NOT PUBLIC API! NOTHING TO SEE HERE!!!**
 #[doc(hidden)]
 impl Diagnostic {
-    pub fn double_spanned(start: Span, end: Span, level: Level, message: String) -> Self {
-        Diagnostic {
-            level,
-            start,
-            end,
-            msg: message,
-            suggestions: vec![],
-            children: vec![],
-        }
-    }
-
-    pub fn double_span_error(mut self, start: Span, end: Span, msg: String) -> Self {
-        self.children.push((start, end, msg));
-        self
-    }
-
     pub fn span_suggestion(self, span: Span, suggestion: &str, msg: String) -> Self {
         match suggestion {
             "help" | "hint" => self.span_help(span, msg),
@@ -153,11 +225,10 @@
         }
 
         fn diag_to_tokens(
-            start: Span,
-            end: Span,
+            span_range: SpanRange,
             level: &Level,
             msg: &str,
-            suggestions: &[(SuggestionKind, String, Option<Span>)],
+            suggestions: &[(SuggestionKind, String, Option<SpanRange>)],
         ) -> TokenStream {
             if *level == Level::Warning {
                 return TokenStream::new();
@@ -181,14 +252,14 @@
                 Cow::Owned(message)
             };
 
-            let msg = syn::LitStr::new(&*message, end);
-            let group = quote_spanned!(end=> { #msg } );
-            quote_spanned!(start=> compile_error!#group)
+            let mut msg = proc_macro2::Literal::string(&message);
+            msg.set_span(span_range.last);
+            let group = quote_spanned!(span_range.last=> { #msg } );
+            quote_spanned!(span_range.first=> compile_error!#group)
         }
 
         ts.extend(diag_to_tokens(
-            self.start,
-            self.end,
+            self.span_range,
             &self.level,
             &self.msg,
             &self.suggestions,
@@ -196,7 +267,7 @@
         ts.extend(
             self.children
                 .iter()
-                .map(|(start, end, msg)| diag_to_tokens(*start, *end, &Level::Error, &msg, &[])),
+                .map(|(span_range, msg)| diag_to_tokens(*span_range, &Level::Error, &msg, &[])),
         );
     }
 }
@@ -216,12 +287,13 @@
     }
 }
 
+#[cfg(feature = "syn-error")]
 impl From<syn::Error> for Diagnostic {
     fn from(err: syn::Error) -> Self {
         use proc_macro2::{Delimiter, TokenTree};
 
-        fn gut_error(ts: &mut impl Iterator<Item = TokenTree>) -> Option<(Span, Span, String)> {
-            let start = match ts.next() {
+        fn gut_error(ts: &mut impl Iterator<Item = TokenTree>) -> Option<(SpanRange, String)> {
+            let first = match ts.next() {
                 // compile_error
                 None => return None,
                 Some(tt) => tt.span(),
@@ -253,23 +325,23 @@
                 _ => unreachable!(),
             };
 
-            let end = lit.span();
+            let last = lit.span();
             let mut msg = lit.to_string();
 
             // "abc" => abc
             msg.pop();
             msg.remove(0);
 
-            Some((start, end, msg))
+            Some((SpanRange { first, last }, msg))
         }
 
         let mut ts = err.to_compile_error().into_iter();
 
-        let (start, end, msg) = gut_error(&mut ts).unwrap();
-        let mut res = Diagnostic::double_spanned(start, end, Level::Error, msg);
+        let (span_range, msg) = gut_error(&mut ts).unwrap();
+        let mut res = Diagnostic::spanned_range(span_range, Level::Error, msg);
 
-        while let Some((start, end, msg)) = gut_error(&mut ts) {
-            res = res.double_span_error(start, end, msg);
+        while let Some((span_range, msg)) = gut_error(&mut ts) {
+            res = res.span_range_error(span_range, msg);
         }
 
         res
diff --git a/src/imp/delegate.rs b/src/imp/delegate.rs
index c096803..07def2b 100644
--- a/src/imp/delegate.rs
+++ b/src/imp/delegate.rs
@@ -24,14 +24,13 @@
 pub(crate) fn emit_diagnostic(diag: Diagnostic) {
     let Diagnostic {
         level,
-        start,
-        end,
+        span_range,
         msg,
         suggestions,
         children,
     } = diag;
 
-    let span = start.join(end).unwrap_or(start);
+    let span = span_range.collapse().unwrap();
 
     let level = match level {
         Level::Warning => PLevel::Warning,
@@ -42,19 +41,23 @@
         _ => unreachable!(),
     };
 
-    let mut res = PDiag::spanned(span.unwrap(), level, msg);
+    let mut res = PDiag::spanned(span, level, msg);
 
     for (kind, msg, span) in suggestions {
         res = match (kind, span) {
-            (SuggestionKind::Note, Some(span)) => res.span_note(span.unwrap(), msg),
-            (SuggestionKind::Help, Some(span)) => res.span_help(span.unwrap(), msg),
+            (SuggestionKind::Note, Some(span_range)) => {
+                res.span_note(span_range.collapse().unwrap(), msg)
+            }
+            (SuggestionKind::Help, Some(span_range)) => {
+                res.span_help(span_range.collapse().unwrap(), msg)
+            }
             (SuggestionKind::Note, None) => res.note(msg),
             (SuggestionKind::Help, None) => res.help(msg),
         }
     }
 
-    for (start, end, msg) in children {
-        let span = start.join(end).unwrap_or(start).unwrap();
+    for (span_range, msg) in children {
+        let span = span_range.collapse().unwrap();
         res = res.span_error(span, msg);
     }
 
diff --git a/src/lib.rs b/src/lib.rs
index 0af2de3..fb867fd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,7 +3,29 @@
 //! This crate aims to make error reporting in proc-macros simple and easy to use.
 //! Migrate from `panic!`-based errors for as little effort as possible!
 //!
-//! Also, there's ability to [append a dummy token stream](dummy/index.html) to your errors.
+//! (Also, you can explicitly [append a dummy token stream](dummy/index.html) to your errors).
+//!
+//! To achieve his, this crate serves as a tiny shim around `proc_macro::Diagnostic` and
+//! `compile_error!`. It detects the best way of emitting available based on compiler's version.
+//! When the underlying diagnostic type is finally stabilized, this crate will simply be
+//! delegating to it requiring no changes in your code!
+//!
+//! So you can just use this crate and have *both* some of `proc_macro::Diagnostic` functionality
+//! available on stable ahead of time *and* your error-reporting code future-proof.
+//!
+//! ## Cargo features
+//!
+//! This crate provides *enabled by default* `syn-error` feature that gates
+//! `impl From<syn::Error> for Diagnostic` conversion. If you don't use `syn` and want
+//! to cut off some of compilation time, you can disable it via
+//!
+//! ```toml
+//! [dependencies]
+//! proc-macro-error = { version = "1", default-features = false }
+//! ```
+//!
+//! ***Please note that disabling this feature makes sense only if you don't depend on `syn`
+//! directly or indirectly, and you very likely do.**
 //!
 //! ## Real world examples
 //!
@@ -253,19 +275,16 @@
 #![forbid(unsafe_code)]
 #![allow(clippy::needless_doctest_main)]
 
-// reexports for use in macros
-#[doc(hidden)]
-pub extern crate proc_macro;
-#[doc(hidden)]
-pub extern crate proc_macro2;
+extern crate proc_macro;
 
 pub use crate::{
-    diagnostic::{Diagnostic, Level},
+    diagnostic::{Diagnostic, DiagnosticExt, Level},
     dummy::{append_dummy, set_dummy},
 };
 pub use proc_macro_error_attr::proc_macro_error;
 
-use quote::quote;
+use proc_macro2::Span;
+use quote::{quote, ToTokens};
 
 use std::cell::Cell;
 use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
@@ -274,6 +293,7 @@
 
 mod diagnostic;
 mod macros;
+mod sealed;
 
 #[cfg(use_fallback)]
 #[path = "imp/fallback.rs"]
@@ -283,6 +303,57 @@
 #[path = "imp/delegate.rs"]
 mod imp;
 
+#[derive(Debug, Clone, Copy)]
+pub struct SpanRange {
+    pub first: Span,
+    pub last: Span,
+}
+
+impl SpanRange {
+    /// Create a range with the `first` and `last` spans being the same.
+    pub fn single_span(span: Span) -> Self {
+        SpanRange {
+            first: span,
+            last: span,
+        }
+    }
+
+    /// Create a `SpanRange` resolving at call site.
+    pub fn call_site() -> Self {
+        SpanRange::single_span(Span::call_site())
+    }
+
+    /// Construct span range from a `TokenStream`. This method always preserves all the
+    /// range.
+    ///
+    /// ### Note
+    ///
+    /// If the stream is empty, the result is `SpanRange::call_site()`. If the stream
+    /// consists of only one `TokenTree`, the result is `SpanRange::single_span(tt.span())`
+    /// that doesn't lose anything.
+    pub fn from_tokens(ts: &dyn ToTokens) -> Self {
+        let mut spans = ts.to_token_stream().into_iter().map(|tt| tt.span());
+        let first = spans.next().unwrap_or_else(|| Span::call_site());
+        let last = spans.last().unwrap_or(first);
+
+        SpanRange { first, last }
+    }
+
+    /// Join two span ranges. The resulting range will start at `self.first` and end at
+    /// `other.last`.
+    pub fn join_range(self, other: SpanRange) -> Self {
+        SpanRange {
+            first: self.first,
+            last: other.last,
+        }
+    }
+
+    /// Collapse the range into single span, preserving as much information as possible.
+    pub fn collapse(self) -> Span {
+        self.first.join(self.last).unwrap_or(self.first)
+    }
+}
+
 /// This traits expands `Result<T, Into<Diagnostic>>` with some handy shortcuts.
 pub trait ResultExt {
     type Ok;
@@ -419,48 +490,71 @@
 /// **ALL THE STUFF INSIDE IS NOT PUBLIC API!!!**
 #[doc(hidden)]
 pub mod __export {
+    // reexports for use in macros
+    pub extern crate proc_macro;
+    pub extern crate proc_macro2;
+
     use proc_macro2::Span;
     use quote::ToTokens;
 
+    use crate::SpanRange;
+
     // inspired by
     // https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md#simple-application
 
-    pub trait DoubleSpanToTokens {
+    pub trait SpanAsSpanRange {
         #[allow(non_snake_case)]
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span);
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
     }
 
-    pub trait DoubleSpanSingleSpan2 {
+    pub trait Span2AsSpanRange {
         #[allow(non_snake_case)]
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span);
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
     }
 
-    pub trait DoubleSpanSingleSpan {
+    pub trait ToTokensAsSpanRange {
         #[allow(non_snake_case)]
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span);
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
     }
 
-    impl<T: ToTokens> DoubleSpanToTokens for &T {
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span) {
+    pub trait SpanRangeAsSpanRange {
+        #[allow(non_snake_case)]
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange;
+    }
+
+    impl<T: ToTokens> ToTokensAsSpanRange for &T {
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
             let mut ts = self.to_token_stream().into_iter();
-            let start = ts
+            let first = ts
                 .next()
                 .map(|tt| tt.span())
                 .unwrap_or_else(Span::call_site);
-            let end = ts.last().map(|tt| tt.span()).unwrap_or(start);
-            (start, end)
+            let last = ts.last().map(|tt| tt.span()).unwrap_or(first);
+            SpanRange { first, last }
         }
     }
 
-    impl DoubleSpanSingleSpan2 for Span {
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span) {
-            (*self, *self)
+    impl Span2AsSpanRange for Span {
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
+            SpanRange {
+                first: *self,
+                last: *self,
+            }
         }
     }
 
-    impl DoubleSpanSingleSpan for proc_macro::Span {
-        fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span) {
-            (self.clone().into(), self.clone().into())
+    impl SpanAsSpanRange for proc_macro::Span {
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
+            SpanRange {
+                first: self.clone().into(),
+                last: self.clone().into(),
+            }
+        }
+    }
+
+    impl SpanRangeAsSpanRange for SpanRange {
+        fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange {
+            *self
         }
     }
 }
diff --git a/src/macros.rs b/src/macros.rs
index ccf6547..747b684 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -15,12 +15,17 @@
     // span, message, help
     ($span:expr, $level:expr, $fmt:expr, $($args:expr),+ ; $($rest:tt)+) => {{
         #[allow(unused_imports)]
-        use $crate::__export::{DoubleSpanToTokens, DoubleSpanSingleSpan, DoubleSpanSingleSpan2};
-        let (start, end) = (&$span).FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS();
+        use $crate::__export::{
+            ToTokensAsSpanRange,
+            Span2AsSpanRange,
+            SpanAsSpanRange,
+            SpanRangeAsSpanRange
+        };
+        use $crate::DiagnosticExt;
+        let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
 
-        let diag = $crate::Diagnostic::double_spanned(
-            start,
-            end,
+        let diag = $crate::Diagnostic::spanned_range(
+            span_range,
             $level,
             format!($fmt, $($args),*)
         );
@@ -30,10 +35,16 @@
 
     ($span:expr, $level:expr, $msg:expr ; $($rest:tt)+) => {{
         #[allow(unused_imports)]
-        use $crate::__export::{DoubleSpanToTokens, DoubleSpanSingleSpan, DoubleSpanSingleSpan2};
-        let (start, end) = (&$span).FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS();
+        use $crate::__export::{
+            ToTokensAsSpanRange,
+            Span2AsSpanRange,
+            SpanAsSpanRange,
+            SpanRangeAsSpanRange
+        };
+        use $crate::DiagnosticExt;
+        let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
 
-        let diag = $crate::Diagnostic::double_spanned(start, end, $level, $msg.to_string());
+        let diag = $crate::Diagnostic::spanned_range(span_range, $level, $msg.to_string());
         $crate::__pme__suggestions!(diag $($rest)*);
         diag
     }};
@@ -41,12 +52,17 @@
     // span, message, no help
     ($span:expr, $level:expr, $fmt:expr, $($args:expr),+) => {{
         #[allow(unused_imports)]
-        use $crate::__export::{DoubleSpanToTokens, DoubleSpanSingleSpan, DoubleSpanSingleSpan2};
-        let (start, end) = (&$span).FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS();
+        use $crate::__export::{
+            ToTokensAsSpanRange,
+            Span2AsSpanRange,
+            SpanAsSpanRange,
+            SpanRangeAsSpanRange
+        };
+        use $crate::DiagnosticExt;
+        let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
 
-        $crate::Diagnostic::double_spanned(
-            start,
-            end,
+        $crate::Diagnostic::spanned_range(
+            span_range,
             $level,
             format!($fmt, $($args),*)
         )
@@ -54,10 +70,16 @@
 
     ($span:expr, $level:expr, $msg:expr) => {{
         #[allow(unused_imports)]
-        use $crate::__export::{DoubleSpanToTokens, DoubleSpanSingleSpan, DoubleSpanSingleSpan2};
-        let (start, end) = (&$span).FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS();
+        use $crate::__export::{
+            ToTokensAsSpanRange,
+            Span2AsSpanRange,
+            SpanAsSpanRange,
+            SpanRangeAsSpanRange
+        };
+        use $crate::DiagnosticExt;
+        let span_range = (&$span).FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange();
 
-        $crate::Diagnostic::double_spanned(start, end, $level, $msg.to_string())
+        $crate::Diagnostic::spanned_range(span_range, $level, $msg.to_string())
     }};
 
 
@@ -104,7 +126,7 @@
 #[macro_export]
 macro_rules! abort_call_site {
     ($($tts:tt)*) => {
-        $crate::abort!($crate::proc_macro2::Span::call_site(), $($tts)*)
+        $crate::abort!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
     };
 }
 
@@ -136,7 +158,7 @@
 #[macro_export]
 macro_rules! emit_call_site_error {
     ($($tts:tt)*) => {
-        $crate::emit_error!($crate::proc_macro2::Span::call_site(), $($tts)*)
+        $crate::emit_error!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
     };
 }
 
@@ -166,7 +188,7 @@
 #[macro_export]
 macro_rules! emit_call_site_warning {
     ($($tts:tt)*) => {{
-        $crate::emit_warning!($crate::proc_macro2::Span()::call_site(), $($tts)*)
+        $crate::emit_warning!($crate::__export::proc_macro2::Span::call_site(), $($tts)*)
     }};
 }
 
diff --git a/src/sealed.rs b/src/sealed.rs
new file mode 100644
index 0000000..a2d5081
--- /dev/null
+++ b/src/sealed.rs
@@ -0,0 +1,3 @@
+pub trait Sealed {}
+
+impl Sealed for crate::Diagnostic {}
diff --git a/tests/ui/abort.rs b/tests/ui/abort.rs
index 5b33b57..f631182 100644
--- a/tests/ui/abort.rs
+++ b/tests/ui/abort.rs
@@ -6,5 +6,6 @@
 abort_format!(one, two);
 direct_abort!(one, two);
 abort_notes!(one, two);
+abort_call_site_test!(one, two);
 
 fn main() {}
diff --git a/tests/ui/abort.stderr b/tests/ui/abort.stderr
index d3e2738..c5399d9 100644
--- a/tests/ui/abort.stderr
+++ b/tests/ui/abort.stderr
@@ -38,3 +38,11 @@
   |
 8 | abort_notes!(one, two);
   |              ^^^
+
+error: abort_call_site! test
+ --> $DIR/abort.rs:9:1
+  |
+9 | abort_call_site_test!(one, two);
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/emit.rs b/tests/ui/emit.rs
index 81d4e0b..c5c7db0 100644
--- a/tests/ui/emit.rs
+++ b/tests/ui/emit.rs
@@ -1,7 +1,7 @@
 extern crate test_crate;
 use test_crate::*;
 
-emit!(one, two, three, four);
+emit!(one, two, three, four, five);
 emit_notes!(one, two);
 
 fn main() {}
diff --git a/tests/ui/emit.stderr b/tests/ui/emit.stderr
index 3c4c3e8..9484bd6 100644
--- a/tests/ui/emit.stderr
+++ b/tests/ui/emit.stderr
@@ -1,27 +1,35 @@
 error: emit!(span, from) test
  --> $DIR/emit.rs:4:7
   |
-4 | emit!(one, two, three, four);
+4 | emit!(one, two, three, four, five);
   |       ^^^
 
 error: emit!(span, expr1, expr2) test
  --> $DIR/emit.rs:4:12
   |
-4 | emit!(one, two, three, four);
+4 | emit!(one, two, three, four, five);
   |            ^^^
 
 error: emit!(span, single_expr) test
  --> $DIR/emit.rs:4:17
   |
-4 | emit!(one, two, three, four);
+4 | emit!(one, two, three, four, five);
   |                 ^^^^^
 
 error: Diagnostic::emit() test
  --> $DIR/emit.rs:4:24
   |
-4 | emit!(one, two, three, four);
+4 | emit!(one, two, three, four, five);
   |                        ^^^^
 
+error: emit_call_site_error!(expr) test
+ --> $DIR/emit.rs:4:1
+  |
+4 | emit!(one, two, three, four, five);
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: This is an error
 
   = note: simple note
diff --git a/tests/ui/explicit_span_range.rs b/tests/ui/explicit_span_range.rs
new file mode 100644
index 0000000..82bbebc
--- /dev/null
+++ b/tests/ui/explicit_span_range.rs
@@ -0,0 +1,6 @@
+extern crate test_crate;
+use test_crate::*;
+
+explicit_span_range!(one, two, three, four);
+
+fn main() {}
diff --git a/tests/ui/explicit_span_range.stderr b/tests/ui/explicit_span_range.stderr
new file mode 100644
index 0000000..781a71e
--- /dev/null
+++ b/tests/ui/explicit_span_range.stderr
@@ -0,0 +1,5 @@
+error: explicit SpanRange
+ --> $DIR/explicit_span_range.rs:4:22
+  |
+4 | explicit_span_range!(one, two, three, four);
+  |                      ^^^^^^^^^^^^^^^
diff --git a/tests/ui/misuse.stderr b/tests/ui/misuse.stderr
index 46e281a..8eaf645 100644
--- a/tests/ui/misuse.stderr
+++ b/tests/ui/misuse.stderr
@@ -1,14 +1,13 @@
-error[E0599]: no method named `FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS` found for reference `&Foo` in the current scope
+error[E0599]: no method named `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange` found for reference `&Foo` in the current scope
  --> $DIR/misuse.rs:8:5
   |
+4 | struct Foo;
+  | ----------- doesn't satisfy `Foo: quote::to_tokens::ToTokens`
+...
 8 |     abort!(Foo, "BOOM");
   |     ^^^^^^^^^^^^^^^^^^^^ method not found in `&Foo`
   |
-  = note: the method `FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS` exists but the following trait bounds were not satisfied:
-          `&Foo : proc_macro_error::__export::DoubleSpanToTokens`
-  = help: items from traits can only be used if the trait is implemented and in scope
-  = note: the following traits define an item `FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS`, perhaps you need to implement one of them:
-          candidate #1: `proc_macro_error::__export::DoubleSpanToTokens`
-          candidate #2: `proc_macro_error::__export::DoubleSpanSingleSpan2`
-          candidate #3: `proc_macro_error::__export::DoubleSpanSingleSpan`
-  = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+  = note: the method `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange` exists but the following trait bounds were not satisfied:
+          `Foo: quote::to_tokens::ToTokens`
+          which is required by `&Foo: proc_macro_error::__export::ToTokensAsSpanRange`
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/not_proc_macro.stderr b/tests/ui/not_proc_macro.stderr
index 35c14b8..f19f01b 100644
--- a/tests/ui/not_proc_macro.stderr
+++ b/tests/ui/not_proc_macro.stderr
@@ -1,8 +1,10 @@
-error: #[proc_macro_error] attribute can be used only with a proc-macro
+error: #[proc_macro_error] attribute can be used only with procedural macros
 
-  = hint: if you are really sure that #[proc_macro_error] should be applied to this exact function use #[proc_macro_error(allow_not_macro)]
+  = hint: if you are really sure that #[proc_macro_error] should be applied to this exact function, use #[proc_macro_error(allow_not_macro)]
 
  --> $DIR/not_proc_macro.rs:3:1
   |
 3 | #[proc_macro_error]
   | ^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/option_ext.stderr b/tests/ui/option_ext.stderr
index f63abc0..91b151e 100644
--- a/tests/ui/option_ext.stderr
+++ b/tests/ui/option_ext.stderr
@@ -2,4 +2,6 @@
  --> $DIR/option_ext.rs:4:1
   |
 4 | option_ext!(one, two);
-  | ^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation
+  | ^^^^^^^^^^^^^^^^^^^^^^
+  |
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/proc_macro_hack.stderr b/tests/ui/proc_macro_hack.stderr
index 7f0876b..0e984f9 100644
--- a/tests/ui/proc_macro_hack.stderr
+++ b/tests/ui/proc_macro_hack.stderr
@@ -3,12 +3,16 @@
   |
 8 |     let nine = add_one!(two) + add_one!(2 + 3);
   |                         ^^^
+  |
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: BOOM
  --> $DIR/proc_macro_hack.rs:8:41
   |
 8 |     let nine = add_one!(two) + add_one!(2 + 3);
   |                                         ^^^^^
+  |
+  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: unreachable expression
  --> $DIR/proc_macro_hack.rs:8:32
@@ -19,4 +23,4 @@
   |                any code following this expression is unreachable
   |
   = note: `#[warn(unreachable_code)]` on by default
-  = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+  = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)