diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index c139cfeaebf..0e05cce35e2 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -783,6 +783,10 @@ impl<'a> ExtCtxt<'a> { pub fn span_err(&self, sp: Span, msg: &str) { self.parse_sess.span_diagnostic.span_err(sp, msg); } + pub fn mut_span_err(&self, sp: Span, msg: &str) + -> DiagnosticBuilder<'a> { + self.parse_sess.span_diagnostic.mut_span_err(sp, msg) + } pub fn span_warn(&self, sp: Span, msg: &str) { self.parse_sess.span_diagnostic.span_warn(sp, msg); } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index de9c085cc78..5deb4c3cc00 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -282,6 +282,32 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let expansion = self.expand_invoc(invoc, ext); self.collect_invocations(expansion, &[]) } else if let InvocationKind::Attr { attr: None, traits, item } = invoc.kind { + let derive_allowed = match item { + Annotatable::Item(ref item) => match item.node { + ast::ItemKind::Struct(..) | + ast::ItemKind::Enum(..) | + ast::ItemKind::Union(..) => true, + _ => false, + }, + _ => false, + }; + if !derive_allowed { + let attr = item.attrs().iter() + .find(|attr| attr.check_name("derive")) + .expect("`derive` attribute should exist"); + let span = attr.span; + let mut err = self.cx.mut_span_err(span, + "`derive` may only be applied to \ + structs, enums and unions"); + if let ast::AttrStyle::Inner = attr.style { + let trait_list = traits.iter() + .map(|t| format!("{}", t)).collect::>(); + let suggestion = format!("#[derive({})]", trait_list.join(", ")); + err.span_suggestion(span, "try an outer attribute", suggestion); + } + err.emit(); + } + let item = item .map_attrs(|mut attrs| { attrs.retain(|a| a.path != "derive"); attrs }); let item_with_markers = diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index d701810e2e9..5c1ca19d635 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -428,8 +428,9 @@ impl<'a> TraitDef<'a> { } } _ => { - cx.span_err(mitem.span, - "`derive` may only be applied to structs, enums and unions"); + // Non-ADT derive is an error, but it should have been + // set earlier; see + // libsyntax/ext/expand.rs:MacroExpander::expand() return; } }; @@ -448,8 +449,10 @@ impl<'a> TraitDef<'a> { push(Annotatable::Item(P(ast::Item { attrs: attrs, ..(*newitem).clone() }))) } _ => { - cx.span_err(mitem.span, - "`derive` may only be applied to structs and enums"); + // Non-Item derive is an error, but it should have been + // set earlier; see + // libsyntax/ext/expand.rs:MacroExpander::expand() + return; } } } diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive-2.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive-2.rs index be82d0a5f6d..2dbc6cb140d 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive-2.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive-2.rs @@ -8,23 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// `#![derive]` is interpreted (and raises errors) when it occurs at -// contexts other than ADT definitions. This test checks cases where -// the derive-macro does not exist. +// This test checks cases where the derive-macro does not exist. -#![derive(x3300)] -//~^ ERROR cannot find derive macro `x3300` in this scope - -#[derive(x3300)] -//~^ ERROR cannot find derive macro `x3300` in this scope mod derive { - mod inner { #![derive(x3300)] } - //~^ ERROR cannot find derive macro `x3300` in this scope - - #[derive(x3300)] - //~^ ERROR cannot find derive macro `x3300` in this scope - fn derive() { } - #[derive(x3300)] //~^ ERROR cannot find derive macro `x3300` in this scope union U { f: i32 } @@ -36,12 +22,4 @@ mod derive { #[derive(x3300)] //~^ ERROR cannot find derive macro `x3300` in this scope struct S; - - #[derive(x3300)] - //~^ ERROR cannot find derive macro `x3300` in this scope - type T = S; - - #[derive(x3300)] - //~^ ERROR cannot find derive macro `x3300` in this scope - impl S { } } diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive.rs index 41c3d0ef561..e5293ebb94d 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-derive.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// `#![derive]` is interpreted (and raises errors) when it occurs at -// contexts other than ADT definitions. This test checks cases where -// the derive-macro exists. +// `#![derive]` raises errors when it occurs at contexts other than ADT +// definitions. #![derive(Debug)] //~^ ERROR `derive` may only be applied to structs, enums and unions diff --git a/src/test/ui/span/issue-43927-non-ADT-derive.rs b/src/test/ui/span/issue-43927-non-ADT-derive.rs new file mode 100644 index 00000000000..cf2a4b8d037 --- /dev/null +++ b/src/test/ui/span/issue-43927-non-ADT-derive.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +#![derive(Debug, PartialEq, Eq)] // should be an outer attribute! +struct DerivedOn; + +fn main() {} diff --git a/src/test/ui/span/issue-43927-non-ADT-derive.stderr b/src/test/ui/span/issue-43927-non-ADT-derive.stderr new file mode 100644 index 00000000000..a0485bed2f4 --- /dev/null +++ b/src/test/ui/span/issue-43927-non-ADT-derive.stderr @@ -0,0 +1,8 @@ +error: `derive` may only be applied to structs, enums and unions + --> $DIR/issue-43927-non-ADT-derive.rs:13:1 + | +13 | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try an outer attribute: `#[derive(Debug, PartialEq, Eq)]` + +error: aborting due to previous error +