diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2d1835514d425..639994ed14d86 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -5753,13 +5753,21 @@ impl<'a> LoweringContext<'a> { } fn maybe_lint_bare_trait(&self, span: Span, id: NodeId, is_global: bool) { - self.sess.buffer_lint_with_diagnostic( - builtin::BARE_TRAIT_OBJECTS, - id, - span, - "trait objects without an explicit `dyn` are deprecated", - builtin::BuiltinLintDiagnostics::BareTraitObject(span, is_global), - ) + // FIXME(davidtwco): This is a hack to detect macros which produce spans of the + // call site which do not have a macro backtrace. See #61963. + let is_macro_callsite = self.sess.source_map() + .span_to_snippet(span) + .map(|snippet| snippet.starts_with("#[")) + .unwrap_or(true); + if !is_macro_callsite { + self.sess.buffer_lint_with_diagnostic( + builtin::BARE_TRAIT_OBJECTS, + id, + span, + "trait objects without an explicit `dyn` are deprecated", + builtin::BuiltinLintDiagnostics::BareTraitObject(span, is_global), + ) + } } fn wrap_in_try_constructor( diff --git a/src/test/ui/suggestions/auxiliary/issue-61963-1.rs b/src/test/ui/suggestions/auxiliary/issue-61963-1.rs new file mode 100644 index 0000000000000..6c2df7e84e07c --- /dev/null +++ b/src/test/ui/suggestions/auxiliary/issue-61963-1.rs @@ -0,0 +1,40 @@ +// force-host +// no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Group, TokenStream, TokenTree}; + +// This macro exists as part of a reproduction of #61963 but without using quote/syn/proc_macro2. + +#[proc_macro_derive(DomObject)] +pub fn expand_token_stream(input: TokenStream) -> TokenStream { + // Construct a dummy span - `#0 bytes(0..0)` - which is present in the input because + // of the specially crafted generated tokens in the `attribute-crate` proc-macro. + let dummy_span = input.clone().into_iter().nth(0).unwrap().span(); + + // Define what the macro would output if constructed properly from the source using syn/quote. + let output: TokenStream = "impl Bar for ((), Qux >) { } + impl Bar for ((), Box) { }".parse().unwrap(); + + let mut tokens: Vec<_> = output.into_iter().collect(); + // Adjust token spans to match the original crate (which would use `quote`). Some of the + // generated tokens point to the dummy span. + for token in tokens.iter_mut() { + if let TokenTree::Group(group) = token { + let mut tokens: Vec<_> = group.stream().into_iter().collect(); + for token in tokens.iter_mut().skip(2) { + token.set_span(dummy_span); + } + + let mut stream = TokenStream::new(); + stream.extend(tokens); + *group = Group::new(group.delimiter(), stream); + } + } + + let mut output = TokenStream::new(); + output.extend(tokens); + output +} diff --git a/src/test/ui/suggestions/auxiliary/issue-61963.rs b/src/test/ui/suggestions/auxiliary/issue-61963.rs new file mode 100644 index 0000000000000..e86f1610ab0d0 --- /dev/null +++ b/src/test/ui/suggestions/auxiliary/issue-61963.rs @@ -0,0 +1,41 @@ +// force-host +// no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Group, Spacing, Punct, TokenTree, TokenStream}; + +// This macro exists as part of a reproduction of #61963 but without using quote/syn/proc_macro2. + +#[proc_macro_attribute] +pub fn dom_struct(_: TokenStream, input: TokenStream) -> TokenStream { + // Construct the expected output tokens - the input but with a `#[derive(DomObject)]` applied. + let attributes: TokenStream = + "#[derive(DomObject)]".to_string().parse().unwrap(); + let output: TokenStream = attributes.into_iter() + .chain(input.into_iter()).collect(); + + let mut tokens: Vec<_> = output.into_iter().collect(); + // Adjust the spacing of `>` tokens to match what `quote` would produce. + for token in tokens.iter_mut() { + if let TokenTree::Group(group) = token { + let mut tokens: Vec<_> = group.stream().into_iter().collect(); + for token in tokens.iter_mut() { + if let TokenTree::Punct(p) = token { + if p.as_char() == '>' { + *p = Punct::new('>', Spacing::Alone); + } + } + } + + let mut stream = TokenStream::new(); + stream.extend(tokens); + *group = Group::new(group.delimiter(), stream); + } + } + + let mut output = TokenStream::new(); + output.extend(tokens); + output +} diff --git a/src/test/ui/suggestions/issue-61963.rs b/src/test/ui/suggestions/issue-61963.rs new file mode 100644 index 0000000000000..c9d738f5a283e --- /dev/null +++ b/src/test/ui/suggestions/issue-61963.rs @@ -0,0 +1,24 @@ +// aux-build:issue-61963.rs +// aux-build:issue-61963-1.rs +#![deny(bare_trait_objects)] + +#[macro_use] +extern crate issue_61963; +#[macro_use] +extern crate issue_61963_1; + +// This test checks that the bare trait object lint does not trigger on macro attributes that +// generate code which would trigger the lint. + +pub struct Baz; +pub trait Bar { } +pub struct Qux(T); + +#[dom_struct] +pub struct Foo { + qux: Qux>, + bar: Box, + //~^ ERROR trait objects without an explicit `dyn` are deprecated [bare_trait_objects] +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-61963.stderr b/src/test/ui/suggestions/issue-61963.stderr new file mode 100644 index 0000000000000..46943f40066ff --- /dev/null +++ b/src/test/ui/suggestions/issue-61963.stderr @@ -0,0 +1,14 @@ +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/issue-61963.rs:20:14 + | +LL | bar: Box, + | ^^^ help: use `dyn`: `dyn Bar` + | +note: lint level defined here + --> $DIR/issue-61963.rs:3:9 + | +LL | #![deny(bare_trait_objects)] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error +