diff options
author | Haibo Huang <hhb@google.com> | 2020-09-30 04:48:30 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-09-30 04:48:30 +0000 |
commit | 7c5e37eb1cbc03760ccece0e20ff4a28f2edfe34 (patch) | |
tree | 1e5c4afb87741351207fbb77e2e68f489670f8bc | |
parent | 4f775fbd2186e2cf157ee0cedf266c2189f85091 (diff) | |
parent | eef7c23c35e10d408ff6ee75bfd990539883067f (diff) | |
download | proc-macro-error-7c5e37eb1cbc03760ccece0e20ff4a28f2edfe34.tar.gz |
Upgrade rust/crates/proc-macro-error to 1.0.4 am: eef7c23c35
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/proc-macro-error/+/1414396
Change-Id: I2dc949e737d3d2d9982ceda2d2d29d5310ce46aa
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 12 | ||||
-rw-r--r-- | CHANGELOG.md | 11 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | Cargo.toml.orig | 15 | ||||
-rw-r--r-- | LICENSE-APACHE | 2 | ||||
-rw-r--r-- | LICENSE-MIT | 2 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | src/diagnostic.rs | 170 | ||||
-rw-r--r-- | src/imp/delegate.rs | 19 | ||||
-rw-r--r-- | src/lib.rs | 144 | ||||
-rw-r--r-- | src/macros.rs | 60 | ||||
-rw-r--r-- | src/sealed.rs | 3 | ||||
-rw-r--r-- | tests/ui/abort.rs | 1 | ||||
-rw-r--r-- | tests/ui/abort.stderr | 8 | ||||
-rw-r--r-- | tests/ui/emit.rs | 2 | ||||
-rw-r--r-- | tests/ui/emit.stderr | 16 | ||||
-rw-r--r-- | tests/ui/explicit_span_range.rs | 6 | ||||
-rw-r--r-- | tests/ui/explicit_span_range.stderr | 5 | ||||
-rw-r--r-- | tests/ui/misuse.stderr | 17 | ||||
-rw-r--r-- | tests/ui/not_proc_macro.stderr | 6 | ||||
-rw-r--r-- | tests/ui/option_ext.stderr | 4 | ||||
-rw-r--r-- | tests/ui/proc_macro_hack.stderr | 6 |
24 files changed, 409 insertions, 141 deletions
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" } } @@ -5,6 +5,11 @@ rust_library_host { 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 @@ rust_library_host { } // 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. @@ -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 @@ repository = "https://gitlab.com/CreepySkeleton/proc-macro-error" [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 @@ version = "1" [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 @@ version = "1.0.19" 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 @@ targets = ["x86_64-unknown-linux-gnu"] [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 @@ APPENDIX: How to apply the Apache License to your work. 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 @@ -7,13 +7,13 @@ third_party { } 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 } } @@ -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 @@ be dual licensed as above, without any additional terms or conditions. [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 @@ pub enum Level { #[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 @@ impl Diagnostic { /// 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 @@ impl Diagnostic { /// # 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 @@ impl Diagnostic { /// # 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 @@ impl Diagnostic { /// /// # 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 @@ impl Diagnostic { /// **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 @@ impl ToTokens for Diagnostic { } 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 @@ impl ToTokens for Diagnostic { 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 @@ impl ToTokens for Diagnostic { 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 @@ impl SuggestionKind { } } +#[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 @@ impl From<syn::Error> for Diagnostic { _ => 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 cleanup() -> Vec<Diagnostic> { 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 @@ pub(crate) fn emit_diagnostic(diag: Diagnostic) { _ => 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); } @@ -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 @@ pub mod dummy; mod diagnostic; mod macros; +mod sealed; #[cfg(use_fallback)] #[path = "imp/fallback.rs"] @@ -283,6 +303,57 @@ mod imp; #[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 @@ fn check_correctness() { /// **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 Span2AsSpanRange for Span { + fn FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange(&self) -> SpanRange { + SpanRange { + first: *self, + last: *self, + } } } - impl DoubleSpanSingleSpan2 for Span { - fn FIRST_ARG_MUST_EITHER_BE_SPAN_OR_IMPLEMENT_TO_TOKENS(&self) -> (Span, Span) { - (*self, *self) + 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 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 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 @@ macro_rules! diagnostic { // 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 @@ macro_rules! diagnostic { ($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 @@ macro_rules! diagnostic { // 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 @@ macro_rules! diagnostic { ($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_rules! abort { #[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_rules! emit_error { #[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_rules! emit_warning { #[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_to_string!(one, two); 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 @@ error: This is an error | 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 @@ error: Option::expect_or_abort() test --> $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 @@ error: BOOM | 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 @@ warning: unreachable expression | 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) |