diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index f8d6bedeace25..bca1b76dbb975 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -11,6 +11,7 @@ mod flatten; mod zip; pub use self::chain::Chain; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::flatten::{FlatMap, Flatten}; pub use self::zip::Zip; pub(crate) use self::zip::TrustedRandomAccess; diff --git a/src/libcore/iter/traits/mod.rs b/src/libcore/iter/traits/mod.rs index 000b9fad70b94..cf3013f423c94 100644 --- a/src/libcore/iter/traits/mod.rs +++ b/src/libcore/iter/traits/mod.rs @@ -5,9 +5,11 @@ mod collect; mod accum; mod marker; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::iterator::Iterator; pub use self::double_ended::DoubleEndedIterator; pub use self::exact_size::ExactSizeIterator; pub use self::collect::{FromIterator, IntoIterator, Extend}; pub use self::accum::{Sum, Product}; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::marker::{FusedIterator, TrustedLen}; diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index 8a74c51d3f723..f8f27992b3ea8 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -252,12 +252,14 @@ impl NonMacroAttrKind { } impl Def { + /// Return the `DefId` of this `Def` if it has an id, else panic. pub fn def_id(&self) -> DefId { self.opt_def_id().unwrap_or_else(|| { bug!("attempted .def_id() on invalid def: {:?}", self) }) } + /// Return `Some(..)` with the `DefId` of this `Def` if it has a id, else `None`. pub fn opt_def_id(&self) -> Option { match *self { Def::Fn(id) | Def::Mod(id) | Def::Static(id, _) | @@ -284,6 +286,14 @@ impl Def { } } + /// Return the `DefId` of this `Def` if it represents a module. + pub fn mod_def_id(&self) -> Option { + match *self { + Def::Mod(id) => Some(id), + _ => None, + } + } + /// A human readable name for the def kind ("function", "module", etc.). pub fn kind_name(&self) -> &'static str { match *self { diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 7736d5e795ea0..3baf0f0ea39ff 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -11,16 +11,16 @@ use syntax::ast::NodeId; // Accessibility levels, sorted in ascending order #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum AccessLevel { - // Superset of Reachable used to mark impl Trait items. + /// Superset of `AccessLevel::Reachable` used to mark impl Trait items. ReachableFromImplTrait, - // Exported items + items participating in various kinds of public interfaces, - // but not directly nameable. For example, if function `fn f() -> T {...}` is - // public, then type `T` is reachable. Its values can be obtained by other crates - // even if the type itself is not nameable. + /// Exported items + items participating in various kinds of public interfaces, + /// but not directly nameable. For example, if function `fn f() -> T {...}` is + /// public, then type `T` is reachable. Its values can be obtained by other crates + /// even if the type itself is not nameable. Reachable, - // Public items + items accessible to other crates with help of `pub use` re-exports + /// Public items + items accessible to other crates with help of `pub use` re-exports Exported, - // Items accessible to other crates directly, without help of re-exports + /// Items accessible to other crates directly, without help of re-exports Public, } @@ -31,12 +31,17 @@ pub struct AccessLevels { } impl AccessLevels { + /// See `AccessLevel::Reachable`. pub fn is_reachable(&self, id: Id) -> bool { self.map.get(&id) >= Some(&AccessLevel::Reachable) } + + /// See `AccessLevel::Exported`. pub fn is_exported(&self, id: Id) -> bool { self.map.get(&id) >= Some(&AccessLevel::Exported) } + + /// See `AccessLevel::Public`. pub fn is_public(&self, id: Id) -> bool { self.map.get(&id) >= Some(&AccessLevel::Public) } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index dcbb9ff4a7576..62730bff6533c 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -436,6 +436,43 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { ev: self, } } + + + /// Given the path segments of a `ItemKind::Use`, then we need + /// to update the visibility of the intermediate use so that it isn't linted + /// by `unreachable_pub`. + /// + /// This isn't trivial as `path.def` has the `DefId` of the eventual target + /// of the use statement not of the next intermediate use statement. + /// + /// To do this, consider the last two segments of the path to our intermediate + /// use statement. We expect the penultimate segment to be a module and the + /// last segment to be the name of the item we are exporting. We can then + /// look at the items contained in the module for the use statement with that + /// name and update that item's visibility. + /// + /// FIXME: This solution won't work with glob imports and doesn't respect + /// namespaces. See . + fn update_visibility_of_intermediate_use_statements(&mut self, segments: &[hir::PathSegment]) { + if let Some([module, segment]) = segments.rchunks_exact(2).next() { + if let Some(item) = module.def + .and_then(|def| def.mod_def_id()) + .and_then(|def_id| self.tcx.hir().as_local_node_id(def_id)) + .map(|module_node_id| self.tcx.hir().expect_item(module_node_id)) + { + if let hir::ItemKind::Mod(m) = &item.node { + for item_id in m.item_ids.as_ref() { + let item = self.tcx.hir().expect_item(item_id.id); + let def_id = self.tcx.hir().local_def_id(item_id.id); + if !self.tcx.hygienic_eq(segment.ident, item.ident, def_id) { continue; } + if let hir::ItemKind::Use(..) = item.node { + self.update(item.id, Some(AccessLevel::Exported)); + } + } + } + } + } + } } impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { @@ -522,8 +559,14 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { hir::ItemKind::ExternCrate(..) => {} // All nested items are checked by `visit_item`. hir::ItemKind::Mod(..) => {} - // Re-exports are handled in `visit_mod`. - hir::ItemKind::Use(..) => {} + // Re-exports are handled in `visit_mod`. However, in order to avoid looping over + // all of the items of a mod in `visit_mod` looking for use statements, we handle + // making sure that intermediate use statements have their visibilities updated here. + hir::ItemKind::Use(ref path, _) => { + if item_level.is_some() { + self.update_visibility_of_intermediate_use_statements(path.segments.as_ref()); + } + } // The interface is empty. hir::ItemKind::GlobalAsm(..) => {} hir::ItemKind::Existential(..) => { diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index fb5b6c97689d0..abf575aed6725 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -15,11 +15,13 @@ use syntax::ast::{self, Ident}; use syntax::attr; use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, Determinacy}; -use syntax::ext::base::{Annotatable, MacroKind, SyntaxExtension}; +use syntax::ext::base::{MacroKind, SyntaxExtension}; use syntax::ext::expand::{AstFragment, Invocation, InvocationKind}; use syntax::ext::hygiene::{self, Mark}; use syntax::ext::tt::macro_rules; -use syntax::feature_gate::{feature_err, is_builtin_attr_name, GateIssue}; +use syntax::feature_gate::{ + feature_err, is_builtin_attr_name, AttributeGate, GateIssue, Stability, BUILTIN_ATTRIBUTES, +}; use syntax::symbol::{Symbol, keywords}; use syntax::visit::Visitor; use syntax::util::lev_distance::find_best_match_for_name; @@ -127,9 +129,9 @@ impl<'a> base::Resolver for Resolver<'a> { mark } - fn resolve_dollar_crates(&mut self, annotatable: &Annotatable) { - pub struct ResolveDollarCrates<'a, 'b: 'a> { - pub resolver: &'a mut Resolver<'b>, + fn resolve_dollar_crates(&mut self, fragment: &AstFragment) { + struct ResolveDollarCrates<'a, 'b: 'a> { + resolver: &'a mut Resolver<'b> } impl<'a> Visitor<'a> for ResolveDollarCrates<'a, '_> { fn visit_ident(&mut self, ident: Ident) { @@ -144,7 +146,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn visit_mac(&mut self, _: &ast::Mac) {} } - annotatable.visit_with(&mut ResolveDollarCrates { resolver: self }); + fragment.visit_with(&mut ResolveDollarCrates { resolver: self }); } fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, @@ -310,15 +312,18 @@ impl<'a> Resolver<'a> { if !features.rustc_attrs { let msg = "unless otherwise specified, attributes with the prefix \ `rustc_` are reserved for internal compiler diagnostics"; - feature_err(&self.session.parse_sess, "rustc_attrs", path.span, - GateIssue::Language, &msg).emit(); + self.report_unknown_attribute(path.span, &name, msg, "rustc_attrs"); } } else if !features.custom_attribute { let msg = format!("The attribute `{}` is currently unknown to the \ compiler and may have meaning added to it in the \ future", path); - feature_err(&self.session.parse_sess, "custom_attribute", path.span, - GateIssue::Language, &msg).emit(); + self.report_unknown_attribute( + path.span, + &name, + &msg, + "custom_attribute", + ); } } } else { @@ -339,6 +344,61 @@ impl<'a> Resolver<'a> { Ok((def, self.get_macro(def))) } + fn report_unknown_attribute(&self, span: Span, name: &str, msg: &str, feature: &str) { + let mut err = feature_err( + &self.session.parse_sess, + feature, + span, + GateIssue::Language, + &msg, + ); + + let features = self.session.features_untracked(); + + let attr_candidates = BUILTIN_ATTRIBUTES + .iter() + .filter_map(|(name, _, _, gate)| { + if name.starts_with("rustc_") && !features.rustc_attrs { + return None; + } + + match gate { + AttributeGate::Gated(Stability::Unstable, ..) + if self.session.opts.unstable_features.is_nightly_build() => + { + Some(name) + } + AttributeGate::Gated(Stability::Deprecated(..), ..) => Some(name), + AttributeGate::Ungated => Some(name), + _ => None, + } + }) + .map(|name| Symbol::intern(name)) + .chain( + // Add built-in macro attributes as well. + self.builtin_macros.iter().filter_map(|(name, binding)| { + match binding.macro_kind() { + Some(MacroKind::Attr) => Some(*name), + _ => None, + } + }), + ) + .collect::>(); + + let lev_suggestion = find_best_match_for_name(attr_candidates.iter(), &name, None); + + if let Some(suggestion) = lev_suggestion { + err.span_suggestion( + span, + "a built-in attribute with a similar name exists", + suggestion.to_string(), + Applicability::MaybeIncorrect, + ); + } + + err.emit(); + } + pub fn resolve_macro_to_def_inner( &mut self, path: &ast::Path, diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index fd55897522bf7..9a04c9d60b868 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -537,11 +537,8 @@ impl<'a> Resolver<'a> { primary_binding: &'a NameBinding<'a>, secondary_binding: &'a NameBinding<'a>) -> &'a NameBinding<'a> { self.arenas.alloc_name_binding(NameBinding { - kind: primary_binding.kind.clone(), ambiguity: Some((secondary_binding, kind)), - vis: primary_binding.vis, - span: primary_binding.span, - expansion: primary_binding.expansion, + ..primary_binding.clone() }) } diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 52a30967a2310..6935ecde791f8 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -410,10 +410,6 @@ kbd { color: #ccc; } -.impl-items code { - background-color: rgba(0, 0, 0, 0); -} - #sidebar-toggle { background-color: #565656; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index d20fea666e61d..306e8dc15d893 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -405,10 +405,6 @@ kbd { color: #999; } -.impl-items code { - background-color: rgba(0, 0, 0, 0); -} - #sidebar-toggle { background-color: #F1F1F1; } diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index f398a2a6225ce..2961328b16096 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -54,6 +54,7 @@ cfg_if! { cfg_if! { if #[cfg(any(unix, target_os = "redox"))] { // On unix we'll document what's already available + #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as unix_ext; } else if #[cfg(any(target_os = "cloudabi", target_arch = "wasm32", diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index a55b32c08a303..eb8e0c1c8ac66 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -607,7 +607,7 @@ impl Builder { pub fn spawn(f: F) -> JoinHandle where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static { - Builder::new().spawn(f).unwrap() + Builder::new().spawn(f).expect("failed to spawn thread") } /// Gets a handle to the thread that invokes it. diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 2793754e1033a..09e7e57f78cfa 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,7 +14,6 @@ use parse::token; use ptr::P; use smallvec::SmallVec; use symbol::{keywords, Ident, Symbol}; -use visit::Visitor; use ThinVec; use rustc_data_structures::fx::FxHashMap; @@ -136,17 +135,6 @@ impl Annotatable { _ => false, } } - - pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { - match self { - Annotatable::Item(item) => visitor.visit_item(item), - Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item), - Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item), - Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item), - Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), - Annotatable::Expr(expr) => visitor.visit_expr(expr), - } - } } // A more flexible ItemDecorator. @@ -742,7 +730,7 @@ pub trait Resolver { fn next_node_id(&mut self) -> ast::NodeId; fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; - fn resolve_dollar_crates(&mut self, annotatable: &Annotatable); + fn resolve_dollar_crates(&mut self, fragment: &AstFragment); fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, derives: &[Mark]); fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc); @@ -776,7 +764,7 @@ impl Resolver for DummyResolver { fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } - fn resolve_dollar_crates(&mut self, _annotatable: &Annotatable) {} + fn resolve_dollar_crates(&mut self, _fragment: &AstFragment) {} fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment, _derives: &[Mark]) {} fn add_builtin(&mut self, _ident: ast::Ident, _ext: Lrc) {} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d1f7b4df9bea4..1b4b44270ad06 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -443,6 +443,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { /// prepares data for resolving paths of macro invocations. fn collect_invocations(&mut self, fragment: AstFragment, derives: &[Mark]) -> (AstFragment, Vec) { + // Resolve `$crate`s in the fragment for pretty-printing. + self.cx.resolver.resolve_dollar_crates(&fragment); + let (fragment_with_placeholders, invocations) = { let mut collector = InvocationCollector { cfg: StripUnconfigured { @@ -574,8 +577,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Some(invoc.fragment_kind.expect_from_annotatables(items)) } AttrProcMacro(ref mac, ..) => { - // Resolve `$crate`s in case we have to go though stringification. - self.cx.resolver.resolve_dollar_crates(&item); self.gate_proc_macro_attr_item(attr.span, &item); let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item { Annotatable::Item(item) => token::NtItem(item), @@ -917,8 +918,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { match *ext { ProcMacroDerive(ref ext, ..) => { - // Resolve `$crate`s in case we have to go though stringification. - self.cx.resolver.resolve_dollar_crates(&item); invoc.expansion_data.mark.set_expn_info(expn_info); let span = span.with_ctxt(self.cx.backtrace()); let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this diff --git a/src/test/pretty/dollar-crate.pp b/src/test/pretty/dollar-crate.pp new file mode 100644 index 0000000000000..3d2d949be2b2e --- /dev/null +++ b/src/test/pretty/dollar-crate.pp @@ -0,0 +1,18 @@ +#![feature(prelude_import)] +#![no_std] +#[prelude_import] +use ::std::prelude::v1::*; +#[macro_use] +extern crate std; +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:dollar-crate.pp + +fn main() { + { + ::std::io::_print(::std::fmt::Arguments::new_v1(&["rust\n"], + &match () { + () => [], + })); + }; +} diff --git a/src/test/pretty/dollar-crate.rs b/src/test/pretty/dollar-crate.rs new file mode 100644 index 0000000000000..e46bc7f4859a7 --- /dev/null +++ b/src/test/pretty/dollar-crate.rs @@ -0,0 +1,7 @@ +// pretty-compare-only +// pretty-mode:expanded +// pp-exact:dollar-crate.pp + +fn main() { + println!("rust"); +} diff --git a/src/test/ui/issues/issue-49074.stderr b/src/test/ui/issues/issue-49074.stderr index d4648270f2d30..a25d8ee352686 100644 --- a/src/test/ui/issues/issue-49074.stderr +++ b/src/test/ui/issues/issue-49074.stderr @@ -2,7 +2,7 @@ error[E0658]: The attribute `marco_use` is currently unknown to the compiler and --> $DIR/issue-49074.rs:3:3 | LL | #[marco_use] // typo - | ^^^^^^^^^ + | ^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_use` | = help: add #![feature(custom_attribute)] to the crate attributes to enable diff --git a/src/test/ui/issues/issue-57410-1.rs b/src/test/ui/issues/issue-57410-1.rs new file mode 100644 index 0000000000000..dab77bd660ca0 --- /dev/null +++ b/src/test/ui/issues/issue-57410-1.rs @@ -0,0 +1,18 @@ +// compile-pass + +// Originally from #53925. +// Tests that the `unreachable_pub` lint doesn't fire for `pub self::bar::Bar`. + +#![deny(unreachable_pub)] + +mod foo { + mod bar { + pub struct Bar; + } + + pub use self::bar::Bar; +} + +pub use foo::Bar; + +fn main() {} diff --git a/src/test/ui/issues/issue-57410.rs b/src/test/ui/issues/issue-57410.rs new file mode 100644 index 0000000000000..0d697e5619d24 --- /dev/null +++ b/src/test/ui/issues/issue-57410.rs @@ -0,0 +1,17 @@ +// compile-pass + +// Tests that the `unreachable_pub` lint doesn't fire for `pub self::imp::f`. + +#![deny(unreachable_pub)] + +mod m { + mod imp { + pub fn f() {} + } + + pub use self::imp::f; +} + +pub use self::m::f; + +fn main() {} diff --git a/src/test/ui/macros/macro-reexport-removed.stderr b/src/test/ui/macros/macro-reexport-removed.stderr index 7c3555a92ed8b..6cfec3ee762dd 100644 --- a/src/test/ui/macros/macro-reexport-removed.stderr +++ b/src/test/ui/macros/macro-reexport-removed.stderr @@ -14,7 +14,7 @@ error[E0658]: The attribute `macro_reexport` is currently unknown to the compile --> $DIR/macro-reexport-removed.rs:5:3 | LL | #[macro_reexport(macro_one)] //~ ERROR attribute `macro_reexport` is currently unknown - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_export` | = help: add #![feature(custom_attribute)] to the crate attributes to enable diff --git a/src/test/ui/proc-macro/derive-still-gated.stderr b/src/test/ui/proc-macro/derive-still-gated.stderr index d54a593f78311..ece1b6212914d 100644 --- a/src/test/ui/proc-macro/derive-still-gated.stderr +++ b/src/test/ui/proc-macro/derive-still-gated.stderr @@ -2,7 +2,7 @@ error[E0658]: The attribute `derive_A` is currently unknown to the compiler and --> $DIR/derive-still-gated.rs:8:3 | LL | #[derive_A] //~ ERROR attribute `derive_A` is currently unknown - | ^^^^^^^^ + | ^^^^^^^^ help: a built-in attribute with a similar name exists: `derive` | = help: add #![feature(custom_attribute)] to the crate attributes to enable diff --git a/src/test/ui/suggestions/attribute-typos.rs b/src/test/ui/suggestions/attribute-typos.rs new file mode 100644 index 0000000000000..13c6308b97e85 --- /dev/null +++ b/src/test/ui/suggestions/attribute-typos.rs @@ -0,0 +1,13 @@ +#[deprcated] //~ ERROR E0658 +fn foo() {} //~| HELP a built-in attribute with a similar name exists + //~| SUGGESTION deprecated + //~| HELP add #![feature(custom_attribute)] to the crate attributes to enable + +#[tests] //~ ERROR E0658 +fn bar() {} //~| HELP a built-in attribute with a similar name exists + //~| SUGGESTION test + //~| HELP add #![feature(custom_attribute)] to the crate attributes to enable + +#[rustc_err] //~ ERROR E0658 +fn main() {} //~| HELP add #![feature(rustc_attrs)] to the crate attributes to enable + // don't suggest rustc attributes diff --git a/src/test/ui/suggestions/attribute-typos.stderr b/src/test/ui/suggestions/attribute-typos.stderr new file mode 100644 index 0000000000000..e40da787e96ca --- /dev/null +++ b/src/test/ui/suggestions/attribute-typos.stderr @@ -0,0 +1,27 @@ +error[E0658]: unless otherwise specified, attributes with the prefix `rustc_` are reserved for internal compiler diagnostics (see issue #29642) + --> $DIR/attribute-typos.rs:11:3 + | +LL | #[rustc_err] //~ ERROR E0658 + | ^^^^^^^^^ + | + = help: add #![feature(rustc_attrs)] to the crate attributes to enable + +error[E0658]: The attribute `tests` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) + --> $DIR/attribute-typos.rs:6:3 + | +LL | #[tests] //~ ERROR E0658 + | ^^^^^ help: a built-in attribute with a similar name exists: `test` + | + = help: add #![feature(custom_attribute)] to the crate attributes to enable + +error[E0658]: The attribute `deprcated` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) + --> $DIR/attribute-typos.rs:1:3 + | +LL | #[deprcated] //~ ERROR E0658 + | ^^^^^^^^^ help: a built-in attribute with a similar name exists: `deprecated` + | + = help: add #![feature(custom_attribute)] to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`.