diff options
Diffstat (limited to 'compiler/rustc_passes')
19 files changed, 2552 insertions, 1344 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a2ac329f2..27a57adf9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,18 +4,24 @@ //! conflicts between multiple such attributes attached to the same //! item. -use crate::errors; +use crate::errors::{ + self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr, + OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint, +}; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID}; +use rustc_hir::{ + self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, +}; use rustc_hir::{MethodKind, Target}; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ @@ -34,8 +40,8 @@ pub(crate) fn target_from_impl_item<'tcx>( match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, hir::ImplItemKind::Fn(..) => { - let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id()); - let containing_item = tcx.hir().expect_item(parent_hir_id); + let parent_def_id = tcx.hir().get_parent_item(impl_item.hir_id()).def_id; + let containing_item = tcx.hir().expect_item(parent_def_id); let containing_impl_is_for_trait = match &containing_item.kind { hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(), _ => bug!("parent of an ImplItem must be an Impl"), @@ -46,7 +52,7 @@ pub(crate) fn target_from_impl_item<'tcx>( Target::Method(MethodKind::Inherent) } } - hir::ImplItemKind::TyAlias(..) => Target::AssocTy, + hir::ImplItemKind::Type(..) => Target::AssocTy, } } @@ -130,6 +136,7 @@ impl CheckAttrVisitor<'_> { | sym::rustc_if_this_changed | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr), sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target), + sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target), sym::const_trait => self.check_const_trait(attr, span, target), sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target), sym::must_use => self.check_must_use(hir_id, &attr, span, target), @@ -146,6 +153,7 @@ impl CheckAttrVisitor<'_> { | sym::stable | sym::rustc_allowed_through_unstable_modules | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), + sym::link_ordinal => self.check_link_ordinal(&attr, span, target), _ => true, }; is_valid &= attr_is_valid; @@ -159,18 +167,19 @@ impl CheckAttrVisitor<'_> { sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), sym::deprecated => self.check_deprecated(hir_id, attr, span, target), sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), - sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]), + sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), sym::macro_export => self.check_macro_export(hir_id, attr, target), sym::ignore | sym::should_panic | sym::proc_macro_derive => { - self.check_generic_attr(hir_id, attr, target, &[Target::Fn]) + self.check_generic_attr(hir_id, attr, target, Target::Fn) } sym::automatically_derived => { - self.check_generic_attr(hir_id, attr, target, &[Target::Impl]) + self.check_generic_attr(hir_id, attr, target, Target::Impl) } sym::no_implicit_prelude => { - self.check_generic_attr(hir_id, attr, target, &[Target::Mod]) + self.check_generic_attr(hir_id, attr, target, Target::Mod) } + sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), _ => {} } @@ -209,7 +218,14 @@ impl CheckAttrVisitor<'_> { } // FIXME(@lcnr): this doesn't belong here. - if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) { + if matches!( + target, + Target::Closure + | Target::Fn + | Target::Method(_) + | Target::ForeignFn + | Target::ForeignStatic + ) { self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); } @@ -252,7 +268,7 @@ impl CheckAttrVisitor<'_> { } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with // just a lint, because we previously erroneously allowed it and some crates used it - // accidentally, to to be compatible with crates depending on them, we can't throw an + // accidentally, to be compatible with crates depending on them, we can't throw an // error here. Target::AssocConst => { self.tcx.emit_spanned_lint( @@ -338,29 +354,18 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, attr: &Attribute, target: Target, - allowed_targets: &[Target], + allowed_target: Target, ) { - if !allowed_targets.iter().any(|t| t == &target) { - let name = attr.name_or_empty(); - let mut i = allowed_targets.iter(); - // Pluralize - let b = i.next().map_or_else(String::new, |t| t.to_string() + "s"); - let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| { - if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 { - b.push_str(", and "); - } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 { - b.push_str(" and "); - } else { - b.push_str(", "); - } - // Pluralize - b.push_str(&(allowed_target.to_string() + "s")); - b - }); - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!("`#[{name}]` only has an effect on {}", supported_names)) - .emit(); - }); + if target != allowed_target { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + OnlyHasEffectOn { + attr_name: attr.name_or_empty(), + target_name: allowed_target.name().replace(" ", "_"), + }, + ); } } @@ -371,7 +376,7 @@ impl CheckAttrVisitor<'_> { | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked"); @@ -402,6 +407,38 @@ impl CheckAttrVisitor<'_> { } } + /// Debugging aid for `object_lifetime_default` query. + fn check_object_lifetime_default(&self, hir_id: HirId) { + let tcx = self.tcx; + if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) { + for p in generics.params { + let hir::GenericParamKind::Type { .. } = p.kind else { continue }; + let param_id = tcx.hir().local_def_id(p.hir_id); + let default = tcx.object_lifetime_default(param_id); + let repr = match default { + ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(), + ObjectLifetimeDefault::Static => "'static".to_owned(), + ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), + ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), + }; + tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr }); + } + } + } + + /// Checks if `#[collapse_debuginfo]` is applied to a macro. + fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::MacroDef => true, + _ => { + self.tcx + .sess + .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); + false + } + } + } + /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid. fn check_track_caller( &self, @@ -419,7 +456,7 @@ impl CheckAttrVisitor<'_> { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[track_caller]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { for attr in attrs { @@ -448,7 +485,7 @@ impl CheckAttrVisitor<'_> { Target::Struct | Target::Enum | Target::Variant => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[non_exhaustive]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); @@ -470,7 +507,7 @@ impl CheckAttrVisitor<'_> { Target::Trait => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[marker]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); @@ -529,7 +566,7 @@ impl CheckAttrVisitor<'_> { } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[target_feature]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); @@ -597,8 +634,8 @@ impl CheckAttrVisitor<'_> { let span = meta.span(); if let Some(location) = match target { Target::AssocTy => { - let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); - let containing_item = self.tcx.hir().expect_item(parent_hir_id); + let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id; + let containing_item = self.tcx.hir().expect_item(parent_def_id); if Target::from_item(containing_item) == Target::Impl { Some("type alias in implementation block") } else { @@ -606,8 +643,8 @@ impl CheckAttrVisitor<'_> { } } Target::AssocConst => { - let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); - let containing_item = self.tcx.hir().expect_item(parent_hir_id); + let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id; + let containing_item = self.tcx.hir().expect_item(parent_def_id); // We can't link to trait impl's consts. let err = "associated constant in trait implementation block"; match containing_item.kind { @@ -632,6 +669,7 @@ impl CheckAttrVisitor<'_> { | Target::GlobalAsm | Target::TyAlias | Target::OpaqueTy + | Target::ImplTraitPlaceholder | Target::Enum | Target::Variant | Target::Struct @@ -644,7 +682,9 @@ impl CheckAttrVisitor<'_> { | Target::ForeignStatic | Target::ForeignTy | Target::GenericParam(..) - | Target::MacroDef => None, + | Target::MacroDef + | Target::PatField + | Target::ExprField => None, } { tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); return false; @@ -782,8 +822,8 @@ impl CheckAttrVisitor<'_> { if let Some((prev_inline, prev_span)) = *specified_inline { if do_inline != prev_inline { let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); - spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first); - spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second); + spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first); + spans.push_span_label(meta.span(), fluent::passes_doc_inline_conflict_second); self.tcx.sess.emit_err(errors::DocKeywordConflict { spans }); return false; } @@ -829,25 +869,31 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { - self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { - let mut err = lint.build(fluent::passes::attr_crate_level); - if attr.style == AttrStyle::Outer - && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID - { - if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { - src.insert(1, '!'); - err.span_suggestion_verbose( - attr.span, - fluent::passes::suggestion, - src, - Applicability::MaybeIncorrect, - ); - } else { - err.span_help(attr.span, fluent::passes::help); + self.tcx.struct_span_lint_hir( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + fluent::passes_attr_crate_level, + |err| { + if attr.style == AttrStyle::Outer + && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID + { + if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { + src.insert(1, '!'); + err.span_suggestion_verbose( + attr.span, + fluent::suggestion, + src, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help(attr.span, fluent::help); + } } - } - err.note(fluent::passes::note).emit(); - }); + err.note(fluent::note); + err + }, + ); return false; } true @@ -888,6 +934,22 @@ impl CheckAttrVisitor<'_> { is_valid } + /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. + /// Returns `true` if valid. + fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + if meta.meta_item_list().is_some() { + true + } else { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocCfgHideTakesList, + ); + false + } + } + /// Runs various checks on `#[doc]` attributes. Returns `true` if valid. /// /// `specified_inline` should be initialized to `None` and kept for the scope @@ -941,6 +1003,13 @@ impl CheckAttrVisitor<'_> { is_valid = false; } + sym::cfg_hide + if !self.check_attr_crate_level(attr, meta, hir_id) + || !self.check_doc_cfg_hide(meta, hir_id) => + { + is_valid = false; + } + sym::inline | sym::no_inline if !self.check_doc_inline( attr, @@ -1159,7 +1228,7 @@ impl CheckAttrVisitor<'_> { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[cold]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold"); @@ -1201,7 +1270,7 @@ impl CheckAttrVisitor<'_> { Target::ForeignFn | Target::ForeignStatic => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[link_name]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name"); @@ -1235,7 +1304,7 @@ impl CheckAttrVisitor<'_> { Target::ExternCrate => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[no_link]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); @@ -1265,7 +1334,7 @@ impl CheckAttrVisitor<'_> { Target::Method(..) if self.is_impl_item(hir_id) => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[export_name]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); @@ -1457,7 +1526,7 @@ impl CheckAttrVisitor<'_> { Target::Static | Target::Fn | Target::Method(..) => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[link_section]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section"); @@ -1482,7 +1551,7 @@ impl CheckAttrVisitor<'_> { Target::Method(..) if self.is_impl_item(hir_id) => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[no_mangle]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle"); @@ -1548,12 +1617,17 @@ impl CheckAttrVisitor<'_> { continue; } - let (article, allowed_targets) = match hint.name_or_empty() { + match hint.name_or_empty() { sym::C => { is_c = true; match target { Target::Struct | Target::Union | Target::Enum => continue, - _ => ("a", "struct, enum, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { + hint_span: hint.span(), + span, + }); + } } } sym::align => { @@ -1569,12 +1643,20 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Union | Target::Enum | Target::Fn => continue, - _ => ("a", "struct, enum, function, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion { + hint_span: hint.span(), + span, + }); + } } } sym::packed => { if target != Target::Struct && target != Target::Union { - ("a", "struct or union") + self.tcx.sess.emit_err(AttrApplication::StructUnion { + hint_span: hint.span(), + span, + }); } else { continue; } @@ -1582,7 +1664,9 @@ impl CheckAttrVisitor<'_> { sym::simd => { is_simd = true; if target != Target::Struct { - ("a", "struct") + self.tcx + .sess + .emit_err(AttrApplication::Struct { hint_span: hint.span(), span }); } else { continue; } @@ -1591,7 +1675,12 @@ impl CheckAttrVisitor<'_> { is_transparent = true; match target { Target::Struct | Target::Union | Target::Enum => continue, - _ => ("a", "struct, enum, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { + hint_span: hint.span(), + span, + }); + } } } sym::i8 @@ -1608,33 +1697,18 @@ impl CheckAttrVisitor<'_> { | sym::usize => { int_reprs += 1; if target != Target::Enum { - ("an", "enum") + self.tcx + .sess + .emit_err(AttrApplication::Enum { hint_span: hint.span(), span }); } else { continue; } } _ => { - struct_span_err!( - self.tcx.sess, - hint.span(), - E0552, - "unrecognized representation hint" - ) - .emit(); - + self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() }); continue; } }; - - struct_span_err!( - self.tcx.sess, - hint.span(), - E0517, - "{}", - &format!("attribute should be applied to {article} {allowed_targets}") - ) - .span_label(span, &format!("not {article} {allowed_targets}")) - .emit(); } // Just point at all repr hints if there are any incompatibilities. @@ -1644,14 +1718,9 @@ impl CheckAttrVisitor<'_> { // Error on repr(transparent, <anything else>). if is_transparent && hints.len() > 1 { let hint_spans: Vec<_> = hint_spans.clone().collect(); - struct_span_err!( - self.tcx.sess, - hint_spans, - E0692, - "transparent {} cannot have other repr hints", - target - ) - .emit(); + self.tcx + .sess + .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() }); } // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) @@ -1694,7 +1763,7 @@ impl CheckAttrVisitor<'_> { } } Some(_) => { - // This error case is handled in rustc_typeck::collect. + // This error case is handled in rustc_hir_analysis::collect. } None => { // Default case (compiler) when arg isn't defined. @@ -1736,7 +1805,7 @@ impl CheckAttrVisitor<'_> { Target::MacroDef => true, // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm => { self.inline_attr_str_error_without_macro_def( @@ -1803,14 +1872,12 @@ impl CheckAttrVisitor<'_> { match std::fs::File::open(&file) { Ok(_) => true, - Err(err) => { - self.tcx - .sess - .struct_span_err( - meta_item.span, - &format!("couldn't read {}: {}", file.display(), err), - ) - .emit(); + Err(error) => { + self.tcx.sess.emit_err(DebugVisualizerUnreadable { + span: meta_item.span, + file: &file, + error, + }); false } } @@ -1833,7 +1900,7 @@ impl CheckAttrVisitor<'_> { } // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to to be compatible + // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable"); @@ -1886,6 +1953,16 @@ impl CheckAttrVisitor<'_> { } } + fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool { + match target { + Target::ForeignFn | Target::ForeignStatic => true, + _ => { + self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span }); + false + } + } + } + fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) { match target { Target::Closure | Target::Expression | Target::Statement | Target::Arm => { @@ -1985,7 +2062,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // so this lets us continue to run them while maintaining backwards compatibility. // In the long run, the checks should be harmonized. if let ItemKind::Macro(ref macro_def, _) = item.kind { - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } @@ -2048,14 +2125,14 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_expr(self, expr) } - fn visit_variant( - &mut self, - variant: &'tcx hir::Variant<'tcx>, - generics: &'tcx hir::Generics<'tcx>, - item_id: HirId, - ) { + fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { + self.check_attributes(field.hir_id, field.span, Target::ExprField, None); + intravisit::walk_expr_field(self, field) + } + + fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) { self.check_attributes(variant.id, variant.span, Target::Variant, None); - intravisit::walk_variant(self, variant, generics, item_id) + intravisit::walk_variant(self, variant) } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -2063,6 +2140,11 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_param(self, param); } + + fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) { + self.check_attributes(field.hir_id, field.span, Target::PatField, None); + intravisit::walk_pat_field(self, field); + } } fn is_c_like_enum(item: &Item<'_>) -> bool { @@ -2092,6 +2174,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::automatically_derived, sym::start, sym::rustc_main, + sym::unix_sigpipe, sym::derive, sym::test, sym::test_case, @@ -2105,25 +2188,11 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if attr.style == AttrStyle::Inner { for attr_to_check in ATTRS_TO_CHECK { if attr.has_name(*attr_to_check) { - let mut err = tcx.sess.struct_span_err( - attr.span, - &format!( - "`{}` attribute cannot be used at crate level", - attr_to_check.to_ident_string() - ), - ); - // Only emit an error with a suggestion if we can create a - // string out of the attribute span - if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) { - let replacement = src.replace("#!", "#"); - err.span_suggestion_verbose( - attr.span, - "perhaps you meant to use an outer attribute", - replacement, - rustc_errors::Applicability::MachineApplicable, - ); - } - err.emit(); + tcx.sess.emit_err(InvalidAttrAtCrateLevel { + span: attr.span, + snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(), + name: *attr_to_check, + }); } } } diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 70518284c..aa726d6cd 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -8,7 +8,6 @@ //! through, but errors for structured control flow in a `const` should be emitted here. use rustc_attr as attr; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -18,6 +17,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; +use crate::errors::ExprNotAllowedInContext; + /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] enum NonConstExpr { @@ -133,18 +134,22 @@ impl<'tcx> CheckConstVisitor<'tcx> { let const_kind = const_kind.expect("`const_check_violated` may only be called inside a const context"); - let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); - let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); match missing_gates.as_slice() { [] => { - struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(); + tcx.sess.emit_err(ExprNotAllowedInContext { + span, + expr: expr.name(), + context: const_kind.keyword_name(), + }); } [missing_primary, ref missing_secondary @ ..] => { + let msg = + format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg); // If multiple feature gates would be required to enable this expression, include @@ -191,10 +196,6 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.tcx.hir() } - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - intravisit::walk_item(self, item); - } - fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { let kind = Some(hir::ConstContext::Const); self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 1e2fbeb38..753d01f46 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -11,13 +11,15 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::middle::privacy; +use rustc_middle::middle::privacy::Level; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; use std::mem; +use crate::errors::UselessAssignment; + // Any local node that may call something in its body block should be // explored. For example, if it's a live Node::Item that is a // function, then we should explore its block to check for codes that @@ -102,14 +104,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } Res::Def(_, def_id) => self.check_def_id(def_id), - Res::SelfTy { trait_: t, alias_to: i } => { - if let Some(t) = t { - self.check_def_id(t); - } - if let Some((i, _)) = i { - self.check_def_id(i); - } - } + Res::SelfTyParam { trait_: t } => self.check_def_id(t), + Res::SelfTyAlias { alias_to: i, .. } => self.check_def_id(i), Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {} } } @@ -186,18 +182,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && !assign.span.from_expansion() { let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..)); - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::DEAD_CODE, assign.hir_id, assign.span, - |lint| { - lint.build(&format!( - "useless assignment of {} of type `{}` to itself", - if is_field_assign { "field" } else { "variable" }, - self.typeck_results().expr_ty(lhs), - )) - .emit(); - }, + UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) } ) } } @@ -226,19 +215,16 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { lhs: &hir::Pat<'_>, res: Res, pats: &[hir::Pat<'_>], - dotdot: Option<usize>, + dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), }; - let first_n = pats.iter().enumerate().take(dotdot.unwrap_or(pats.len())); + let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); + let first_n = pats.iter().enumerate().take(dotdot); let missing = variant.fields.len() - pats.len(); - let last_n = pats - .iter() - .enumerate() - .skip(dotdot.unwrap_or(pats.len())) - .map(|(idx, pat)| (idx + missing, pat)); + let last_n = pats.iter().enumerate().skip(dotdot).map(|(idx, pat)| (idx + missing, pat)); for (idx, pat) in first_n.chain(last_n) { if let PatKind::Wild = pat.kind { continue; @@ -294,8 +280,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn visit_node(&mut self, node: Node<'tcx>) { - if let Node::ImplItem(hir::ImplItem { def_id, .. }) = node - && self.should_ignore_item(def_id.to_def_id()) + if let Node::ImplItem(hir::ImplItem { owner_id, .. }) = node + && self.should_ignore_item(owner_id.to_def_id()) { return; } @@ -307,7 +293,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { match node { Node::Item(item) => match item.kind { hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => { - let def = self.tcx.adt_def(item.def_id); + let def = self.tcx.adt_def(item.owner_id); self.repr_has_repr_c = def.repr().c(); self.repr_has_repr_simd = def.repr().simd(); @@ -320,7 +306,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_trait_item(self, trait_item); } Node::ImplItem(impl_item) => { - let item = self.tcx.local_parent(impl_item.def_id); + let item = self.tcx.local_parent(impl_item.owner_id.def_id); if self.tcx.impl_trait_ref(item).is_none() { //// If it's a type whose items are live, then it's live, too. //// This is done to handle the case where, for example, the static @@ -368,14 +354,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_variant_data( - &mut self, - def: &'tcx hir::VariantData<'tcx>, - _: Symbol, - _: &hir::Generics<'_>, - _: hir::HirId, - _: rustc_span::Span, - ) { + fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) { let tcx = self.tcx; let has_repr_c = self.repr_has_repr_c; let has_repr_simd = self.repr_has_repr_simd; @@ -384,7 +363,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { if has_repr_c || (f.is_positional() && has_repr_simd) { return Some(def_id); } - if !tcx.visibility(f.hir_id.owner).is_public() { + if !tcx.visibility(f.hir_id.owner.def_id).is_public() { return None; } if tcx.visibility(def_id).is_public() { Some(def_id) } else { None } @@ -457,7 +436,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::OpaqueDef(item_id, _) = ty.kind { + if let TyKind::OpaqueDef(item_id, _, _) = ty.kind { let item = self.tcx.hir().item(item_id); intravisit::walk_item(self, item); } @@ -538,10 +517,10 @@ fn check_item<'tcx>( ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id()); if allow_dead_code { - worklist.push(id.def_id); + worklist.push(id.owner_id.def_id); } - match tcx.def_kind(id.def_id) { + match tcx.def_kind(id.owner_id) { DefKind::Enum => { let item = tcx.hir().item(id); if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { @@ -561,15 +540,15 @@ fn check_item<'tcx>( } } DefKind::Impl => { - let of_trait = tcx.impl_trait_ref(id.def_id); + let of_trait = tcx.impl_trait_ref(id.owner_id); if of_trait.is_some() { - worklist.push(id.def_id); + worklist.push(id.owner_id.def_id); } // get DefIds from another query let local_def_ids = tcx - .associated_item_def_ids(id.def_id) + .associated_item_def_ids(id.owner_id) .iter() .filter_map(|def_id| def_id.as_local()); @@ -587,12 +566,12 @@ fn check_item<'tcx>( if let hir::ItemKind::Struct(ref variant_data, _) = item.kind && let Some(ctor_hir_id) = variant_data.ctor_hir_id() { - struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.def_id); + struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.owner_id.def_id); } } DefKind::GlobalAsm => { // global_asm! is always live. - worklist.push(id.def_id); + worklist.push(id.owner_id.def_id); } _ => {} } @@ -600,12 +579,12 @@ fn check_item<'tcx>( fn check_trait_item<'tcx>(tcx: TyCtxt<'tcx>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) { use hir::TraitItemKind::{Const, Fn}; - if matches!(tcx.def_kind(id.def_id), DefKind::AssocConst | DefKind::AssocFn) { + if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { let trait_item = tcx.hir().trait_item(id); if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_))) && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id()) { - worklist.push(trait_item.def_id); + worklist.push(trait_item.owner_id.def_id); } } } @@ -615,27 +594,24 @@ fn check_foreign_item<'tcx>( worklist: &mut Vec<LocalDefId>, id: hir::ForeignItemId, ) { - if matches!(tcx.def_kind(id.def_id), DefKind::Static(_) | DefKind::Fn) + if matches!(tcx.def_kind(id.owner_id), DefKind::Static(_) | DefKind::Fn) && has_allow_dead_code_or_lang_attr(tcx, id.hir_id()) { - worklist.push(id.def_id); + worklist.push(id.owner_id.def_id); } } fn create_and_seed_worklist<'tcx>( tcx: TyCtxt<'tcx>, ) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) { - let access_levels = &tcx.privacy_access_levels(()); + let effective_visibilities = &tcx.effective_visibilities(()); // see `MarkSymbolVisitor::struct_constructors` let mut struct_constructors = Default::default(); - let mut worklist = access_levels - .map + let mut worklist = effective_visibilities .iter() - .filter_map( - |(&id, &level)| { - if level >= privacy::AccessLevel::Reachable { Some(id) } else { None } - }, - ) + .filter_map(|(&id, effective_vis)| { + effective_vis.is_public_at_level(Level::Reachable).then_some(id) + }) // Seed entry point .chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local())) .collect::<Vec<_>>(); @@ -736,6 +712,26 @@ impl<'tcx> DeadVisitor<'tcx> { }) .collect(); + let descr = tcx.def_kind(first_id).descr(first_id.to_def_id()); + let span_len = dead_codes.len(); + let names = match &names[..] { + _ if span_len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + format!( + "{} and `{last}` ", + names.iter().map(|name| format!("`{name}`")).join(", ") + ) + } + [] => unreachable!(), + }; + let msg = format!( + "{these}{descr}{s} {names}{are} never {participle}", + these = if span_len > 6 { "multiple " } else { "" }, + s = pluralize!(span_len), + are = pluralize!("is", span_len), + ); + tcx.struct_span_lint_hir( if is_positional { lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS @@ -744,27 +740,8 @@ impl<'tcx> DeadVisitor<'tcx> { }, tcx.hir().local_def_id_to_hir_id(first_id), MultiSpan::from_spans(spans.clone()), - |lint| { - let descr = tcx.def_kind(first_id).descr(first_id.to_def_id()); - let span_len = dead_codes.len(); - let names = match &names[..] { - _ if span_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - format!( - "{} and `{last}` ", - names.iter().map(|name| format!("`{name}`")).join(", ") - ) - } - [] => unreachable!(), - }; - let mut err = lint.build(&format!( - "{these}{descr}{s} {names}{are} never {participle}", - these = if span_len > 6 { "multiple " } else { "" }, - s = pluralize!(span_len), - are = pluralize!("is", span_len), - )); - + msg, + |err| { if is_positional { err.multipart_suggestion( &format!( @@ -810,7 +787,7 @@ impl<'tcx> DeadVisitor<'tcx> { ); err.note(&msg); } - err.emit(); + err }, ); } @@ -884,19 +861,19 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) { let module_items = tcx.hir_module_items(module); for item in module_items.items() { - if !live_symbols.contains(&item.def_id) { - let parent = tcx.local_parent(item.def_id); + if !live_symbols.contains(&item.owner_id.def_id) { + let parent = tcx.local_parent(item.owner_id.def_id); if parent != module && !live_symbols.contains(&parent) { // We already have diagnosed something. continue; } - visitor.check_definition(item.def_id); + visitor.check_definition(item.owner_id.def_id); continue; } - let def_kind = tcx.def_kind(item.def_id); + let def_kind = tcx.def_kind(item.owner_id); if let DefKind::Struct | DefKind::Union | DefKind::Enum = def_kind { - let adt = tcx.adt_def(item.def_id); + let adt = tcx.adt_def(item.owner_id); let mut dead_variants = Vec::new(); for variant in adt.variants() { @@ -939,16 +916,21 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) { visitor.warn_dead_fields_and_variants(def_id, "read", dead_fields, is_positional) } - visitor.warn_dead_fields_and_variants(item.def_id, "constructed", dead_variants, false); + visitor.warn_dead_fields_and_variants( + item.owner_id.def_id, + "constructed", + dead_variants, + false, + ); } } for impl_item in module_items.impl_items() { - visitor.check_definition(impl_item.def_id); + visitor.check_definition(impl_item.owner_id.def_id); } for foreign_item in module_items.foreign_items() { - visitor.check_definition(foreign_item.def_id); + visitor.check_definition(foreign_item.owner_id.def_id); } // We do not warn trait items. diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index e08683fe2..253b0a88e 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -13,6 +13,8 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType}; use std::sync::Arc; +use crate::errors::DebugVisualizerUnreadable; + fn check_for_debugger_visualizer<'tcx>( tcx: TyCtxt<'tcx>, hir_id: HirId, @@ -54,13 +56,12 @@ fn check_for_debugger_visualizer<'tcx>( debugger_visualizers .insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type)); } - Err(err) => { - tcx.sess - .struct_span_err( - meta_item.span, - &format!("couldn't read {}: {}", file.display(), err), - ) - .emit(); + Err(error) => { + tcx.sess.emit_err(DebugVisualizerUnreadable { + span: meta_item.span, + file: &file, + error, + }); } } } diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index e6b69d898..a72056e00 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -14,7 +14,9 @@ use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{kw::Empty, sym, Symbol}; + +use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate}; fn observe_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -33,25 +35,22 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item items.id_to_name.insert(item_def_id, name); if let Some(original_def_id) = items.name_to_id.insert(name, item_def_id) { if original_def_id != item_def_id { - let mut err = match tcx.hir().span_if_local(item_def_id) { - Some(span) => tcx - .sess - .struct_span_err(span, &format!("duplicate diagnostic item found: `{name}`.")), - None => tcx.sess.struct_err(&format!( - "duplicate diagnostic item in crate `{}`: `{}`.", - tcx.crate_name(item_def_id.krate), - name - )), - }; - if let Some(span) = tcx.hir().span_if_local(original_def_id) { - err.span_note(span, "the diagnostic item is first defined here"); + let orig_span = tcx.hir().span_if_local(original_def_id); + let orig_crate_name = if orig_span.is_some() { + None } else { - err.note(&format!( - "the diagnostic item is first defined in crate `{}`.", - tcx.crate_name(original_def_id.krate) - )); - } - err.emit(); + Some(tcx.crate_name(original_def_id.krate)) + }; + match tcx.hir().span_if_local(item_def_id) { + Some(span) => tcx.sess.emit_err(DuplicateDiagnosticItem { span, name }), + None => tcx.sess.emit_err(DuplicateDiagnosticItemInCrate { + span: orig_span, + orig_crate_name: orig_crate_name.unwrap_or(Empty), + have_orig_crate_name: orig_crate_name.map(|_| ()), + crate_name: tcx.crate_name(item_def_id.krate), + name, + }), + }; } } } @@ -74,19 +73,19 @@ fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> DiagnosticItems let crate_items = tcx.hir_crate_items(()); for id in crate_items.items() { - observe_item(tcx, &mut diagnostic_items, id.def_id); + observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); } for id in crate_items.trait_items() { - observe_item(tcx, &mut diagnostic_items, id.def_id); + observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); } for id in crate_items.impl_items() { - observe_item(tcx, &mut diagnostic_items, id.def_id); + observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); } for id in crate_items.foreign_items() { - observe_item(tcx, &mut diagnostic_items, id.def_id); + observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); } diagnostic_items diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 7381019a6..5885f45ae 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,14 +1,19 @@ -use rustc_ast::{entry::EntryPointType, Attribute}; -use rustc_errors::struct_span_err; +use rustc_ast::entry::EntryPointType; +use rustc_errors::error_code; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{DefIdTree, TyCtxt}; -use rustc_session::config::{CrateType, EntryFnType}; +use rustc_session::config::{sigpipe, CrateType, EntryFnType}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_span::{Span, Symbol}; + +use crate::errors::{ + AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain, + MultipleStartFunctions, NoMainErr, UnixSigpipeValues, +}; struct EntryContext<'tcx> { tcx: TyCtxt<'tcx>, @@ -57,7 +62,7 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry } else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) { EntryPointType::RustcMainAttr } else { - if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id()) + if let Some(name) = ctxt.tcx.opt_item_name(id.owner_id.to_def_id()) && name == sym::main { if at_root { // This is a top-level function so can be `main`. @@ -71,63 +76,57 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry } } -fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) { - if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) { - ctxt.tcx - .sess - .struct_span_err( - attr.span, - &format!("`{}` attribute can only be used on functions", sym), - ) - .emit(); - } +fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> { + let attrs = ctxt.tcx.hir().attrs(id.hir_id()); + ctxt.tcx.sess.find_by_name(attrs, sym).map(|attr| attr.span) } fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { - let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID); + let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID); match entry_point_type(ctxt, id, at_root) { - EntryPointType::None => (), - _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { - let attrs = ctxt.tcx.hir().attrs(id.hir_id()); - err_if_attr_found(ctxt, attrs, sym::start); - err_if_attr_found(ctxt, attrs, sym::rustc_main); + EntryPointType::None => { + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } + } + _ if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) => { + for attr in [sym::start, sym::rustc_main] { + if let Some(span) = attr_span_by_symbol(ctxt, id, attr) { + ctxt.tcx.sess.emit_err(AttrOnlyInFunctions { span, attr }); + } + } } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { - ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe }); + } + ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id)); } EntryPointType::RustcMainAttr => { if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); + ctxt.attr_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); } else { - struct_span_err!( - ctxt.tcx.sess, - ctxt.tcx.def_span(id.def_id.to_def_id()), - E0137, - "multiple functions with a `#[rustc_main]` attribute" - ) - .span_label( - ctxt.tcx.def_span(id.def_id.to_def_id()), - "additional `#[rustc_main]` function", - ) - .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function") - .emit(); + ctxt.tcx.sess.emit_err(MultipleRustcMain { + span: ctxt.tcx.def_span(id.owner_id.to_def_id()), + first: ctxt.attr_main_fn.unwrap().1, + additional: ctxt.tcx.def_span(id.owner_id.to_def_id()), + }); } } EntryPointType::Start => { + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); + ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); } else { - struct_span_err!( - ctxt.tcx.sess, - ctxt.tcx.def_span(id.def_id.to_def_id()), - E0138, - "multiple `start` functions" - ) - .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") - .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions") - .emit(); + ctxt.tcx.sess.emit_err(MultipleStartFunctions { + span: ctxt.tcx.def_span(id.owner_id), + labeled: ctxt.tcx.def_span(id.owner_id.to_def_id()), + previous: ctxt.start_fn.unwrap().1, + }); } } } @@ -136,18 +135,14 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { if let Some((def_id, _)) = visitor.start_fn { Some((def_id.to_def_id(), EntryFnType::Start)) - } else if let Some((def_id, _)) = visitor.attr_main_fn { - Some((def_id.to_def_id(), EntryFnType::Main)) + } else if let Some((local_def_id, _)) = visitor.attr_main_fn { + let def_id = local_def_id.to_def_id(); + Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })) } else { if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() { // non-local main imports are handled below if let Some(def_id) = def_id.as_local() && matches!(tcx.hir().find_by_def_id(def_id), Some(Node::ForeignItem(_))) { - tcx.sess - .struct_span_err( - tcx.def_span(def_id), - "the `main` function cannot be declared in an `extern` block", - ) - .emit(); + tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) }); return None; } @@ -161,13 +156,34 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, ) .emit(); } - return Some((def_id, EntryFnType::Main)); + return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); } no_main_err(tcx, visitor); None } } +fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { + if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) { + match (attr.value_str(), attr.meta_item_list()) { + (Some(sym::inherit), None) => sigpipe::INHERIT, + (Some(sym::sig_ign), None) => sigpipe::SIG_IGN, + (Some(sym::sig_dfl), None) => sigpipe::SIG_DFL, + (_, Some(_)) => { + // Keep going so that `fn emit_malformed_attribute()` can print + // an excellent error message + sigpipe::DEFAULT + } + _ => { + tcx.sess.emit_err(UnixSigpipeValues { span: attr.span }); + sigpipe::DEFAULT + } + } + } else { + sigpipe::DEFAULT + } +} + fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { let sp = tcx.def_span(CRATE_DEF_ID); if *tcx.sess.parse_sess.reached_eof.borrow() { @@ -178,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { } // There is no main function. - let mut err = struct_span_err!( - tcx.sess, - DUMMY_SP, - E0601, - "`main` function not found in crate `{}`", - tcx.crate_name(LOCAL_CRATE) - ); - let filename = &tcx.sess.local_crate_source_file; - let note = if !visitor.non_main_fns.is_empty() { - for &span in &visitor.non_main_fns { - err.span_note(span, "here is a function named `main`"); - } - err.note("you have one or more functions named `main` not defined at the crate level"); - err.help("consider moving the `main` function definitions"); - // There were some functions named `main` though. Try to give the user a hint. - format!( - "the main function must be defined at the crate level{}", - filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default() - ) - } else if let Some(filename) = filename { - format!("consider adding a `main` function to `{}`", filename.display()) - } else { - String::from("consider adding a `main` function at the crate level") - }; + let mut has_filename = true; + let filename = tcx.sess.local_crate_source_file.clone().unwrap_or_else(|| { + has_filename = false; + Default::default() + }); + let main_def_opt = tcx.resolutions(()).main_def; + let diagnostic_id = error_code!(E0601); + let add_teach_note = tcx.sess.teach(&diagnostic_id); // The file may be empty, which leads to the diagnostic machinery not emitting this // note. This is a relatively simple way to detect that case and emit a span-less // note instead. - if tcx.sess.source_map().lookup_line(sp.hi()).is_ok() { - err.set_span(sp.shrink_to_hi()); - err.span_label(sp.shrink_to_hi(), ¬e); - } else { - err.note(¬e); - } - - if let Some(main_def) = tcx.resolutions(()).main_def && main_def.opt_fn_def_id().is_none(){ - // There is something at `crate::main`, but it is not a function definition. - err.span_label(main_def.span, "non-function item at `crate::main` is found"); - } - - if tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "If you don't know the basics of Rust, you can go look to the Rust Book \ - to get started: https://doc.rust-lang.org/book/", - ); - } - err.emit(); + let file_empty = !tcx.sess.source_map().lookup_line(sp.hi()).is_ok(); + + tcx.sess.emit_err(NoMainErr { + sp, + crate_name: tcx.crate_name(LOCAL_CRATE), + has_filename, + filename, + file_empty, + non_main_fns: visitor.non_main_fns.clone(), + main_def_opt, + add_teach_note, + }); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 5feb0e295..adaaf5392 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1,39 +1,49 @@ -use rustc_errors::{Applicability, MultiSpan}; -use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; -use rustc_span::{Span, Symbol}; +use std::{ + io::Error, + path::{Path, PathBuf}, +}; + +use rustc_ast::Label; +use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan}; +use rustc_hir::{self as hir, ExprKind, Target}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::ty::{MainDefinition, Ty}; +use rustc_span::{Span, Symbol, DUMMY_SP}; + +use crate::lang_items::Duplicate; #[derive(LintDiagnostic)] -#[lint(passes::outer_crate_level_attr)] +#[diag(passes_outer_crate_level_attr)] pub struct OuterCrateLevelAttr; #[derive(LintDiagnostic)] -#[lint(passes::inner_crate_level_attr)] +#[diag(passes_inner_crate_level_attr)] pub struct InnerCrateLevelAttr; #[derive(LintDiagnostic)] -#[lint(passes::ignored_attr_with_macro)] +#[diag(passes_ignored_attr_with_macro)] pub struct IgnoredAttrWithMacro<'a> { pub sym: &'a str, } #[derive(LintDiagnostic)] -#[lint(passes::ignored_attr)] +#[diag(passes_ignored_attr)] pub struct IgnoredAttr<'a> { pub sym: &'a str, } #[derive(LintDiagnostic)] -#[lint(passes::inline_ignored_function_prototype)] +#[diag(passes_inline_ignored_function_prototype)] pub struct IgnoredInlineAttrFnProto; #[derive(LintDiagnostic)] -#[lint(passes::inline_ignored_constants)] -#[warn_] +#[diag(passes_inline_ignored_constants)] +#[warning] #[note] pub struct IgnoredInlineAttrConstants; -#[derive(SessionDiagnostic)] -#[error(passes::inline_not_fn_or_closure, code = "E0518")] +#[derive(Diagnostic)] +#[diag(passes_inline_not_fn_or_closure, code = "E0518")] pub struct InlineNotFnOrClosure { #[primary_span] pub attr_span: Span, @@ -42,19 +52,19 @@ pub struct InlineNotFnOrClosure { } #[derive(LintDiagnostic)] -#[lint(passes::no_coverage_ignored_function_prototype)] +#[diag(passes_no_coverage_ignored_function_prototype)] pub struct IgnoredNoCoverageFnProto; #[derive(LintDiagnostic)] -#[lint(passes::no_coverage_propagate)] +#[diag(passes_no_coverage_propagate)] pub struct IgnoredNoCoveragePropagate; #[derive(LintDiagnostic)] -#[lint(passes::no_coverage_fn_defn)] +#[diag(passes_no_coverage_fn_defn)] pub struct IgnoredNoCoverageFnDefn; -#[derive(SessionDiagnostic)] -#[error(passes::no_coverage_not_coverable, code = "E0788")] +#[derive(Diagnostic)] +#[diag(passes_no_coverage_not_coverable, code = "E0788")] pub struct IgnoredNoCoverageNotCoverable { #[primary_span] pub attr_span: Span, @@ -62,8 +72,8 @@ pub struct IgnoredNoCoverageNotCoverable { pub defn_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::should_be_applied_to_fn)] +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_fn)] pub struct AttrShouldBeAppliedToFn { #[primary_span] pub attr_span: Span, @@ -71,15 +81,15 @@ pub struct AttrShouldBeAppliedToFn { pub defn_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::naked_tracked_caller, code = "E0736")] +#[derive(Diagnostic)] +#[diag(passes_naked_tracked_caller, code = "E0736")] pub struct NakedTrackedCaller { #[primary_span] pub attr_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::should_be_applied_to_fn, code = "E0739")] +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_fn, code = "E0739")] pub struct TrackedCallerWrongLocation { #[primary_span] pub attr_span: Span, @@ -87,8 +97,8 @@ pub struct TrackedCallerWrongLocation { pub defn_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::should_be_applied_to_struct_enum, code = "E0701")] +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_struct_enum, code = "E0701")] pub struct NonExhaustiveWrongLocation { #[primary_span] pub attr_span: Span, @@ -96,8 +106,8 @@ pub struct NonExhaustiveWrongLocation { pub defn_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::should_be_applied_to_trait)] +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_trait)] pub struct AttrShouldBeAppliedToTrait { #[primary_span] pub attr_span: Span, @@ -106,11 +116,11 @@ pub struct AttrShouldBeAppliedToTrait { } #[derive(LintDiagnostic)] -#[lint(passes::target_feature_on_statement)] +#[diag(passes_target_feature_on_statement)] pub struct TargetFeatureOnStatement; -#[derive(SessionDiagnostic)] -#[error(passes::should_be_applied_to_static)] +#[derive(Diagnostic)] +#[diag(passes_should_be_applied_to_static)] pub struct AttrShouldBeAppliedToStatic { #[primary_span] pub attr_span: Span, @@ -118,24 +128,24 @@ pub struct AttrShouldBeAppliedToStatic { pub defn_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_expect_str)] +#[derive(Diagnostic)] +#[diag(passes_doc_expect_str)] pub struct DocExpectStr<'a> { #[primary_span] pub attr_span: Span, pub attr_name: &'a str, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_empty)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_empty)] pub struct DocAliasEmpty<'a> { #[primary_span] pub span: Span, pub attr_str: &'a str, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_bad_char)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_bad_char)] pub struct DocAliasBadChar<'a> { #[primary_span] pub span: Span, @@ -143,16 +153,16 @@ pub struct DocAliasBadChar<'a> { pub char_: char, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_start_end)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_start_end)] pub struct DocAliasStartEnd<'a> { #[primary_span] pub span: Span, pub attr_str: &'a str, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_bad_location)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_bad_location)] pub struct DocAliasBadLocation<'a> { #[primary_span] pub span: Span, @@ -160,8 +170,8 @@ pub struct DocAliasBadLocation<'a> { pub location: &'a str, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_not_an_alias)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_not_an_alias)] pub struct DocAliasNotAnAlias<'a> { #[primary_span] pub span: Span, @@ -169,64 +179,64 @@ pub struct DocAliasNotAnAlias<'a> { } #[derive(LintDiagnostic)] -#[lint(passes::doc_alias_duplicated)] +#[diag(passes_doc_alias_duplicated)] pub struct DocAliasDuplicated { #[label] pub first_defn: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_not_string_literal)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_not_string_literal)] pub struct DocAliasNotStringLiteral { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_alias_malformed)] +#[derive(Diagnostic)] +#[diag(passes_doc_alias_malformed)] pub struct DocAliasMalformed { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_keyword_empty_mod)] +#[derive(Diagnostic)] +#[diag(passes_doc_keyword_empty_mod)] pub struct DocKeywordEmptyMod { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_keyword_not_mod)] +#[derive(Diagnostic)] +#[diag(passes_doc_keyword_not_mod)] pub struct DocKeywordNotMod { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_keyword_invalid_ident)] +#[derive(Diagnostic)] +#[diag(passes_doc_keyword_invalid_ident)] pub struct DocKeywordInvalidIdent { #[primary_span] pub span: Span, pub doc_keyword: Symbol, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_fake_variadic_not_valid)] +#[derive(Diagnostic)] +#[diag(passes_doc_fake_variadic_not_valid)] pub struct DocFakeVariadicNotValid { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_keyword_only_impl)] +#[derive(Diagnostic)] +#[diag(passes_doc_keyword_only_impl)] pub struct DocKeywordOnlyImpl { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_inline_conflict)] +#[derive(Diagnostic)] +#[diag(passes_doc_inline_conflict)] #[help] pub struct DocKeywordConflict { #[primary_span] @@ -234,17 +244,17 @@ pub struct DocKeywordConflict { } #[derive(LintDiagnostic)] -#[lint(passes::doc_inline_only_use)] +#[diag(passes_doc_inline_only_use)] #[note] pub struct DocInlineOnlyUse { #[label] pub attr_span: Span, - #[label(passes::not_a_use_item_label)] + #[label(not_a_use_item_label)] pub item_span: Option<Span>, } -#[derive(SessionDiagnostic)] -#[error(passes::doc_attr_not_crate_level)] +#[derive(Diagnostic)] +#[diag(passes_doc_attr_not_crate_level)] pub struct DocAttrNotCrateLevel<'a> { #[primary_span] pub span: Span, @@ -252,29 +262,33 @@ pub struct DocAttrNotCrateLevel<'a> { } #[derive(LintDiagnostic)] -#[lint(passes::doc_test_unknown)] +#[diag(passes_doc_test_unknown)] pub struct DocTestUnknown { pub path: String, } #[derive(LintDiagnostic)] -#[lint(passes::doc_test_takes_list)] +#[diag(passes_doc_test_takes_list)] pub struct DocTestTakesList; #[derive(LintDiagnostic)] -#[lint(passes::doc_primitive)] +#[diag(passes_doc_cfg_hide_takes_list)] +pub struct DocCfgHideTakesList; + +#[derive(LintDiagnostic)] +#[diag(passes_doc_primitive)] pub struct DocPrimitive; #[derive(LintDiagnostic)] -#[lint(passes::doc_test_unknown_any)] +#[diag(passes_doc_test_unknown_any)] pub struct DocTestUnknownAny { pub path: String, } #[derive(LintDiagnostic)] -#[lint(passes::doc_test_unknown_spotlight)] +#[diag(passes_doc_test_unknown_spotlight)] #[note] -#[note(passes::no_op_note)] +#[note(no_op_note)] pub struct DocTestUnknownSpotlight { pub path: String, #[suggestion_short(applicability = "machine-applicable", code = "notable_trait")] @@ -282,7 +296,7 @@ pub struct DocTestUnknownSpotlight { } #[derive(LintDiagnostic)] -#[lint(passes::doc_test_unknown_include)] +#[diag(passes_doc_test_unknown_include)] pub struct DocTestUnknownInclude { pub path: String, pub value: String, @@ -292,11 +306,11 @@ pub struct DocTestUnknownInclude { } #[derive(LintDiagnostic)] -#[lint(passes::doc_invalid)] +#[diag(passes_doc_invalid)] pub struct DocInvalid; -#[derive(SessionDiagnostic)] -#[error(passes::pass_by_value)] +#[derive(Diagnostic)] +#[diag(passes_pass_by_value)] pub struct PassByValue { #[primary_span] pub attr_span: Span, @@ -304,8 +318,8 @@ pub struct PassByValue { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::allow_incoherent_impl)] +#[derive(Diagnostic)] +#[diag(passes_allow_incoherent_impl)] pub struct AllowIncoherentImpl { #[primary_span] pub attr_span: Span, @@ -313,8 +327,8 @@ pub struct AllowIncoherentImpl { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::has_incoherent_inherent_impl)] +#[derive(Diagnostic)] +#[diag(passes_has_incoherent_inherent_impl)] pub struct HasIncoherentInherentImpl { #[primary_span] pub attr_span: Span, @@ -323,21 +337,21 @@ pub struct HasIncoherentInherentImpl { } #[derive(LintDiagnostic)] -#[lint(passes::must_use_async)] +#[diag(passes_must_use_async)] pub struct MustUseAsync { #[label] pub span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::must_use_no_effect)] +#[diag(passes_must_use_no_effect)] pub struct MustUseNoEffect { pub article: &'static str, pub target: rustc_hir::Target, } -#[derive(SessionDiagnostic)] -#[error(passes::must_not_suspend)] +#[derive(Diagnostic)] +#[diag(passes_must_not_suspend)] pub struct MustNotSuspend { #[primary_span] pub attr_span: Span, @@ -346,24 +360,24 @@ pub struct MustNotSuspend { } #[derive(LintDiagnostic)] -#[lint(passes::cold)] -#[warn_] +#[diag(passes_cold)] +#[warning] pub struct Cold { #[label] pub span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::link)] -#[warn_] +#[diag(passes_link)] +#[warning] pub struct Link { #[label] pub span: Option<Span>, } #[derive(LintDiagnostic)] -#[lint(passes::link_name)] -#[warn_] +#[diag(passes_link_name)] +#[warning] pub struct LinkName<'a> { #[help] pub attr_span: Option<Span>, @@ -372,8 +386,8 @@ pub struct LinkName<'a> { pub value: &'a str, } -#[derive(SessionDiagnostic)] -#[error(passes::no_link)] +#[derive(Diagnostic)] +#[diag(passes_no_link)] pub struct NoLink { #[primary_span] pub attr_span: Span, @@ -381,8 +395,8 @@ pub struct NoLink { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::export_name)] +#[derive(Diagnostic)] +#[diag(passes_export_name)] pub struct ExportName { #[primary_span] pub attr_span: Span, @@ -390,8 +404,8 @@ pub struct ExportName { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_layout_scalar_valid_range_not_struct)] +#[derive(Diagnostic)] +#[diag(passes_rustc_layout_scalar_valid_range_not_struct)] pub struct RustcLayoutScalarValidRangeNotStruct { #[primary_span] pub attr_span: Span, @@ -399,15 +413,15 @@ pub struct RustcLayoutScalarValidRangeNotStruct { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_layout_scalar_valid_range_arg)] +#[derive(Diagnostic)] +#[diag(passes_rustc_layout_scalar_valid_range_arg)] pub struct RustcLayoutScalarValidRangeArg { #[primary_span] pub attr_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_legacy_const_generics_only)] +#[derive(Diagnostic)] +#[diag(passes_rustc_legacy_const_generics_only)] pub struct RustcLegacyConstGenericsOnly { #[primary_span] pub attr_span: Span, @@ -415,8 +429,8 @@ pub struct RustcLegacyConstGenericsOnly { pub param_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_legacy_const_generics_index)] +#[derive(Diagnostic)] +#[diag(passes_rustc_legacy_const_generics_index)] pub struct RustcLegacyConstGenericsIndex { #[primary_span] pub attr_span: Span, @@ -424,8 +438,8 @@ pub struct RustcLegacyConstGenericsIndex { pub generics_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_legacy_const_generics_index_exceed)] +#[derive(Diagnostic)] +#[diag(passes_rustc_legacy_const_generics_index_exceed)] pub struct RustcLegacyConstGenericsIndexExceed { #[primary_span] #[label] @@ -433,75 +447,75 @@ pub struct RustcLegacyConstGenericsIndexExceed { pub arg_count: usize, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_legacy_const_generics_index_negative)] +#[derive(Diagnostic)] +#[diag(passes_rustc_legacy_const_generics_index_negative)] pub struct RustcLegacyConstGenericsIndexNegative { #[primary_span] pub invalid_args: Vec<Span>, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_dirty_clean)] +#[derive(Diagnostic)] +#[diag(passes_rustc_dirty_clean)] pub struct RustcDirtyClean { #[primary_span] pub span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::link_section)] -#[warn_] +#[diag(passes_link_section)] +#[warning] pub struct LinkSection { #[label] pub span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::no_mangle_foreign)] -#[warn_] +#[diag(passes_no_mangle_foreign)] +#[warning] #[note] pub struct NoMangleForeign { #[label] pub span: Span, - #[suggestion(applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, pub foreign_item_kind: &'static str, } #[derive(LintDiagnostic)] -#[lint(passes::no_mangle)] -#[warn_] +#[diag(passes_no_mangle)] +#[warning] pub struct NoMangle { #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::repr_ident, code = "E0565")] +#[derive(Diagnostic)] +#[diag(passes_repr_ident, code = "E0565")] pub struct ReprIdent { #[primary_span] pub span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::repr_conflicting, code = "E0566")] +#[diag(passes_repr_conflicting, code = "E0566")] pub struct ReprConflicting; -#[derive(SessionDiagnostic)] -#[error(passes::used_static)] +#[derive(Diagnostic)] +#[diag(passes_used_static)] pub struct UsedStatic { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::used_compiler_linker)] +#[derive(Diagnostic)] +#[diag(passes_used_compiler_linker)] pub struct UsedCompilerLinker { #[primary_span] pub spans: Vec<Span>, } -#[derive(SessionDiagnostic)] -#[error(passes::allow_internal_unstable)] +#[derive(Diagnostic)] +#[diag(passes_allow_internal_unstable)] pub struct AllowInternalUnstable { #[primary_span] pub attr_span: Span, @@ -509,25 +523,34 @@ pub struct AllowInternalUnstable { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::debug_visualizer_placement)] +#[derive(Diagnostic)] +#[diag(passes_debug_visualizer_placement)] pub struct DebugVisualizerPlacement { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::debug_visualizer_invalid)] -#[note(passes::note_1)] -#[note(passes::note_2)] -#[note(passes::note_3)] +#[derive(Diagnostic)] +#[diag(passes_debug_visualizer_invalid)] +#[note(note_1)] +#[note(note_2)] +#[note(note_3)] pub struct DebugVisualizerInvalid { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_allow_const_fn_unstable)] +#[derive(Diagnostic)] +#[diag(passes_debug_visualizer_unreadable)] +pub struct DebugVisualizerUnreadable<'a> { + #[primary_span] + pub span: Span, + pub file: &'a Path, + pub error: Error, +} + +#[derive(Diagnostic)] +#[diag(passes_rustc_allow_const_fn_unstable)] pub struct RustcAllowConstFnUnstable { #[primary_span] pub attr_span: Span, @@ -535,8 +558,8 @@ pub struct RustcAllowConstFnUnstable { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_std_internal_symbol)] +#[derive(Diagnostic)] +#[diag(passes_rustc_std_internal_symbol)] pub struct RustcStdInternalSymbol { #[primary_span] pub attr_span: Span, @@ -544,59 +567,66 @@ pub struct RustcStdInternalSymbol { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::const_trait)] +#[derive(Diagnostic)] +#[diag(passes_const_trait)] pub struct ConstTrait { #[primary_span] pub attr_span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::stability_promotable)] +#[derive(Diagnostic)] +#[diag(passes_link_ordinal)] +pub struct LinkOrdinal { + #[primary_span] + pub attr_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_stability_promotable)] pub struct StabilityPromotable { #[primary_span] pub attr_span: Span, } #[derive(LintDiagnostic)] -#[lint(passes::deprecated)] +#[diag(passes_deprecated)] pub struct Deprecated; #[derive(LintDiagnostic)] -#[lint(passes::macro_use)] +#[diag(passes_macro_use)] pub struct MacroUse { pub name: Symbol, } #[derive(LintDiagnostic)] -#[lint(passes::macro_export)] +#[diag(passes_macro_export)] pub struct MacroExport; #[derive(LintDiagnostic)] -#[lint(passes::plugin_registrar)] +#[diag(passes_plugin_registrar)] pub struct PluginRegistrar; -#[derive(SessionSubdiagnostic)] +#[derive(Subdiagnostic)] pub enum UnusedNote { - #[note(passes::unused_empty_lints_note)] + #[note(passes_unused_empty_lints_note)] EmptyList { name: Symbol }, - #[note(passes::unused_no_lints_note)] + #[note(passes_unused_no_lints_note)] NoLints { name: Symbol }, - #[note(passes::unused_default_method_body_const_note)] + #[note(passes_unused_default_method_body_const_note)] DefaultMethodBodyConst, } #[derive(LintDiagnostic)] -#[lint(passes::unused)] +#[diag(passes_unused)] pub struct Unused { - #[suggestion(applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, #[subdiagnostic] pub note: UnusedNote, } -#[derive(SessionDiagnostic)] -#[error(passes::non_exported_macro_invalid_attrs, code = "E0518")] +#[derive(Diagnostic)] +#[diag(passes_non_exported_macro_invalid_attrs, code = "E0518")] pub struct NonExportedMacroInvalidAttrs { #[primary_span] #[label] @@ -604,19 +634,18 @@ pub struct NonExportedMacroInvalidAttrs { } #[derive(LintDiagnostic)] -#[lint(passes::unused_duplicate)] +#[diag(passes_unused_duplicate)] pub struct UnusedDuplicate { - #[primary_span] #[suggestion(code = "", applicability = "machine-applicable")] pub this: Span, #[note] pub other: Span, - #[warn_] + #[warning] pub warning: Option<()>, } -#[derive(SessionDiagnostic)] -#[error(passes::unused_multiple)] +#[derive(Diagnostic)] +#[diag(passes_unused_multiple)] pub struct UnusedMultiple { #[primary_span] #[suggestion(code = "", applicability = "machine-applicable")] @@ -626,8 +655,8 @@ pub struct UnusedMultiple { pub name: Symbol, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_lint_opt_ty)] +#[derive(Diagnostic)] +#[diag(passes_rustc_lint_opt_ty)] pub struct RustcLintOptTy { #[primary_span] pub attr_span: Span, @@ -635,11 +664,788 @@ pub struct RustcLintOptTy { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(passes::rustc_lint_opt_deny_field_access)] +#[derive(Diagnostic)] +#[diag(passes_rustc_lint_opt_deny_field_access)] pub struct RustcLintOptDenyFieldAccess { #[primary_span] pub attr_span: Span, #[label] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(passes_collapse_debuginfo)] +pub struct CollapseDebuginfo { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(passes_deprecated_annotation_has_no_effect)] +pub struct DeprecatedAnnotationHasNoEffect { + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_unknown_external_lang_item, code = "E0264")] +pub struct UnknownExternLangItem { + #[primary_span] + pub span: Span, + pub lang_item: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_missing_panic_handler)] +pub struct MissingPanicHandler; + +#[derive(Diagnostic)] +#[diag(passes_alloc_func_required)] +pub struct AllocFuncRequired; + +#[derive(Diagnostic)] +#[diag(passes_missing_alloc_error_handler)] +pub struct MissingAllocErrorHandler; + +#[derive(Diagnostic)] +#[diag(passes_missing_lang_item)] +#[note] +#[help] +pub struct MissingLangItem { + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_lang_item_on_incorrect_target, code = "E0718")] +pub struct LangItemOnIncorrectTarget { + #[primary_span] + #[label] + pub span: Span, + pub name: Symbol, + pub expected_target: Target, + pub actual_target: Target, +} + +#[derive(Diagnostic)] +#[diag(passes_unknown_lang_item, code = "E0522")] +pub struct UnknownLangItem { + #[primary_span] + #[label] + pub span: Span, + pub name: Symbol, +} + +pub struct InvalidAttrAtCrateLevel { + pub span: Span, + pub snippet: Option<String>, + pub name: Symbol, +} + +impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel { + fn into_diagnostic( + self, + handler: &'_ rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_err(rustc_errors::fluent::passes_invalid_attr_at_crate_level); + diag.set_span(self.span); + diag.set_arg("name", self.name); + // Only emit an error with a suggestion if we can create a string out + // of the attribute span + if let Some(src) = self.snippet { + let replacement = src.replace("#!", "#"); + diag.span_suggestion_verbose( + self.span, + rustc_errors::fluent::suggestion, + replacement, + rustc_errors::Applicability::MachineApplicable, + ); + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes_duplicate_diagnostic_item)] +pub struct DuplicateDiagnosticItem { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_duplicate_diagnostic_item_in_crate)] +pub struct DuplicateDiagnosticItemInCrate { + #[note(passes_diagnostic_item_first_defined)] + pub span: Option<Span>, + pub orig_crate_name: Symbol, + #[note] + pub have_orig_crate_name: Option<()>, + pub crate_name: Symbol, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_abi)] +pub struct Abi { + #[primary_span] + pub span: Span, + pub abi: String, +} + +#[derive(Diagnostic)] +#[diag(passes_align)] +pub struct Align { + #[primary_span] + pub span: Span, + pub align: String, +} + +#[derive(Diagnostic)] +#[diag(passes_size)] +pub struct Size { + #[primary_span] + pub span: Span, + pub size: String, +} + +#[derive(Diagnostic)] +#[diag(passes_homogeneous_aggregate)] +pub struct HomogeneousAggregate { + #[primary_span] + pub span: Span, + pub homogeneous_aggregate: String, +} + +#[derive(Diagnostic)] +#[diag(passes_layout_of)] +pub struct LayoutOf { + #[primary_span] + pub span: Span, + pub normalized_ty: String, + pub ty_layout: String, +} + +#[derive(Diagnostic)] +#[diag(passes_unrecognized_field)] +pub struct UnrecognizedField { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_feature_stable_twice, code = "E0711")] +pub struct FeatureStableTwice { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub since: Symbol, + pub prev_since: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_feature_previously_declared, code = "E0711")] +pub struct FeaturePreviouslyDeclared<'a, 'b> { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub declared: &'a str, + pub prev_declared: &'b str, +} + +#[derive(Diagnostic)] +#[diag(passes_expr_not_allowed_in_context, code = "E0744")] +pub struct ExprNotAllowedInContext<'a> { + #[primary_span] + pub span: Span, + pub expr: String, + pub context: &'a str, +} + +pub struct BreakNonLoop<'a> { + pub span: Span, + pub head: Option<Span>, + pub kind: &'a str, + pub suggestion: String, + pub loop_label: Option<Label>, + pub break_label: Option<Label>, + pub break_expr_kind: &'a ExprKind<'a>, + pub break_expr_span: Span, +} + +impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::passes_break_non_loop, + error_code!(E0571), + ); + diag.set_arg("kind", self.kind); + diag.span_label(self.span, rustc_errors::fluent::label); + if let Some(head) = self.head { + diag.span_label(head, rustc_errors::fluent::label2); + } + diag.span_suggestion( + self.span, + rustc_errors::fluent::suggestion, + self.suggestion, + Applicability::MaybeIncorrect, + ); + if let (Some(label), None) = (self.loop_label, self.break_label) { + match self.break_expr_kind { + ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { segments: [segment], res: hir::def::Res::Err, .. }, + )) if label.ident.to_string() == format!("'{}", segment.ident) => { + // This error is redundant, we will have already emitted a + // suggestion to use the label when `segment` wasn't found + // (hence the `Res::Err` check). + diag.delay_as_bug(); + } + _ => { + diag.span_suggestion( + self.break_expr_span, + rustc_errors::fluent::break_expr_suggestion, + label.ident, + Applicability::MaybeIncorrect, + ); + } + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes_continue_labeled_block, code = "E0696")] +pub struct ContinueLabeledBlock { + #[primary_span] + #[label] + pub span: Span, + #[label(block_label)] + pub block_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_break_inside_closure, code = "E0267")] +pub struct BreakInsideClosure<'a> { + #[primary_span] + #[label] + pub span: Span, + #[label(closure_label)] + pub closure_span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_break_inside_async_block, code = "E0267")] +pub struct BreakInsideAsyncBlock<'a> { + #[primary_span] + #[label] + pub span: Span, + #[label(async_block_label)] + pub closure_span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_outside_loop, code = "E0268")] +pub struct OutsideLoop<'a> { + #[primary_span] + #[label] + pub span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_unlabeled_in_labeled_block, code = "E0695")] +pub struct UnlabeledInLabeledBlock<'a> { + #[primary_span] + #[label] + pub span: Span, + pub cf_type: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_unlabeled_cf_in_while_condition, code = "E0590")] +pub struct UnlabeledCfInWhileCondition<'a> { + #[primary_span] + #[label] + pub span: Span, + pub cf_type: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_cannot_inline_naked_function)] +pub struct CannotInlineNakedFunction { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(passes_undefined_naked_function_abi)] +pub struct UndefinedNakedFunctionAbi; + +#[derive(Diagnostic)] +#[diag(passes_no_patterns)] +pub struct NoPatterns { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_params_not_allowed)] +#[help] +pub struct ParamsNotAllowed { + #[primary_span] + pub span: Span, +} + +pub struct NakedFunctionsAsmBlock { + pub span: Span, + pub multiple_asms: Vec<Span>, + pub non_asms: Vec<Span>, +} + +impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::passes_naked_functions_asm_block, + error_code!(E0787), + ); + for span in self.multiple_asms.iter() { + diag.span_label(*span, rustc_errors::fluent::label_multiple_asm); + } + for span in self.non_asms.iter() { + diag.span_label(*span, rustc_errors::fluent::label_non_asm); + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes_naked_functions_operands, code = "E0787")] +pub struct NakedFunctionsOperands { + #[primary_span] + pub unsupported_operands: Vec<Span>, +} + +#[derive(Diagnostic)] +#[diag(passes_naked_functions_asm_options, code = "E0787")] +pub struct NakedFunctionsAsmOptions { + #[primary_span] + pub span: Span, + pub unsupported_options: String, +} + +#[derive(Diagnostic)] +#[diag(passes_naked_functions_must_use_noreturn, code = "E0787")] +pub struct NakedFunctionsMustUseNoreturn { + #[primary_span] + pub span: Span, + #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")] + pub last_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_attr_only_on_main)] +pub struct AttrOnlyOnMain { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_attr_only_on_root_main)] +pub struct AttrOnlyOnRootMain { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_attr_only_in_functions)] +pub struct AttrOnlyInFunctions { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_multiple_rustc_main, code = "E0137")] +pub struct MultipleRustcMain { + #[primary_span] + pub span: Span, + #[label(first)] + pub first: Span, + #[label(additional)] + pub additional: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_multiple_start_functions, code = "E0138")] +pub struct MultipleStartFunctions { + #[primary_span] + pub span: Span, + #[label] + pub labeled: Span, + #[label(previous)] + pub previous: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_extern_main)] +pub struct ExternMain { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_unix_sigpipe_values)] +pub struct UnixSigpipeValues { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_no_main_function, code = "E0601")] +pub struct NoMainFunction { + #[primary_span] + pub span: Span, + pub crate_name: String, +} + +pub struct NoMainErr { + pub sp: Span, + pub crate_name: Symbol, + pub has_filename: bool, + pub filename: PathBuf, + pub file_empty: bool, + pub non_main_fns: Vec<Span>, + pub main_def_opt: Option<MainDefinition>, + pub add_teach_note: bool, +} + +impl<'a> IntoDiagnostic<'a> for NoMainErr { + fn into_diagnostic( + self, + handler: &'a rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + DUMMY_SP, + rustc_errors::fluent::passes_no_main_function, + error_code!(E0601), + ); + diag.set_arg("crate_name", self.crate_name); + diag.set_arg("filename", self.filename); + diag.set_arg("has_filename", self.has_filename); + let note = if !self.non_main_fns.is_empty() { + for &span in &self.non_main_fns { + diag.span_note(span, rustc_errors::fluent::here_is_main); + } + diag.note(rustc_errors::fluent::one_or_more_possible_main); + diag.help(rustc_errors::fluent::consider_moving_main); + // There were some functions named `main` though. Try to give the user a hint. + rustc_errors::fluent::main_must_be_defined_at_crate + } else if self.has_filename { + rustc_errors::fluent::consider_adding_main_to_file + } else { + rustc_errors::fluent::consider_adding_main_at_crate + }; + if self.file_empty { + diag.note(note); + } else { + diag.set_span(self.sp.shrink_to_hi()); + diag.span_label(self.sp.shrink_to_hi(), note); + } + + if let Some(main_def) = self.main_def_opt && main_def.opt_fn_def_id().is_none(){ + // There is something at `crate::main`, but it is not a function definition. + diag.span_label(main_def.span, rustc_errors::fluent::non_function_main); + } + + if self.add_teach_note { + diag.note(rustc_errors::fluent::teach_note); + } + diag + } +} + +pub struct DuplicateLangItem { + pub local_span: Option<Span>, + pub lang_item_name: Symbol, + pub crate_name: Symbol, + pub dependency_of: Symbol, + pub is_local: bool, + pub path: String, + pub first_defined_span: Option<Span>, + pub orig_crate_name: Symbol, + pub orig_dependency_of: Symbol, + pub orig_is_local: bool, + pub orig_path: String, + pub(crate) duplicate: Duplicate, +} + +impl IntoDiagnostic<'_> for DuplicateLangItem { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_err_with_code( + match self.duplicate { + Duplicate::Plain => rustc_errors::fluent::passes_duplicate_lang_item, + + Duplicate::Crate => rustc_errors::fluent::passes_duplicate_lang_item_crate, + Duplicate::CrateDepends => { + rustc_errors::fluent::passes_duplicate_lang_item_crate_depends + } + }, + error_code!(E0152), + ); + diag.set_arg("lang_item_name", self.lang_item_name); + diag.set_arg("crate_name", self.crate_name); + diag.set_arg("dependency_of", self.dependency_of); + diag.set_arg("path", self.path); + diag.set_arg("orig_crate_name", self.orig_crate_name); + diag.set_arg("orig_dependency_of", self.orig_dependency_of); + diag.set_arg("orig_path", self.orig_path); + if let Some(span) = self.local_span { + diag.set_span(span); + } + if let Some(span) = self.first_defined_span { + diag.span_note(span, rustc_errors::fluent::first_defined_span); + } else { + if self.orig_dependency_of.is_empty() { + diag.note(rustc_errors::fluent::first_defined_crate); + } else { + diag.note(rustc_errors::fluent::first_defined_crate_depends); + } + + if self.orig_is_local { + diag.note(rustc_errors::fluent::first_definition_local); + } else { + diag.note(rustc_errors::fluent::first_definition_path); + } + + if self.is_local { + diag.note(rustc_errors::fluent::second_definition_local); + } else { + diag.note(rustc_errors::fluent::second_definition_path); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes_incorrect_target, code = "E0718")] +pub struct IncorrectTarget<'a> { + #[primary_span] + pub span: Span, + #[label] + pub generics_span: Span, + pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` + pub kind: &'static str, + pub num: usize, + pub actual_num: usize, + pub at_least: bool, +} + +#[derive(LintDiagnostic)] +#[diag(passes_useless_assignment)] +pub struct UselessAssignment<'a> { + pub is_field_assign: bool, + pub ty: Ty<'a>, +} + +#[derive(LintDiagnostic)] +#[diag(passes_only_has_effect_on)] +pub struct OnlyHasEffectOn { + pub attr_name: Symbol, + pub target_name: String, +} + +#[derive(Diagnostic)] +#[diag(passes_object_lifetime_err)] +pub struct ObjectLifetimeErr { + #[primary_span] + pub span: Span, + pub repr: String, +} + +#[derive(Diagnostic)] +#[diag(passes_unrecognized_repr_hint, code = "E0552")] +#[help] +pub struct UnrecognizedReprHint { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +pub enum AttrApplication { + #[diag(passes_attr_application_enum, code = "E0517")] + Enum { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes_attr_application_struct, code = "E0517")] + Struct { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes_attr_application_struct_union, code = "E0517")] + StructUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes_attr_application_struct_enum_union, code = "E0517")] + StructEnumUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes_attr_application_struct_enum_function_union, code = "E0517")] + StructEnumFunctionUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(passes_transparent_incompatible, code = "E0692")] +pub struct TransparentIncompatible { + #[primary_span] + pub hint_spans: Vec<Span>, + pub target: String, +} + +#[derive(Diagnostic)] +#[diag(passes_deprecated_attribute, code = "E0549")] +pub struct DeprecatedAttribute { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_useless_stability)] +pub struct UselessStability { + #[primary_span] + #[label] + pub span: Span, + #[label(item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_invalid_stability)] +pub struct InvalidStability { + #[primary_span] + #[label] + pub span: Span, + #[label(item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_cannot_stabilize_deprecated)] +pub struct CannotStabilizeDeprecated { + #[primary_span] + #[label] + pub span: Span, + #[label(item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_invalid_deprecation_version)] +pub struct InvalidDeprecationVersion { + #[primary_span] + #[label] + pub span: Span, + #[label(item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_missing_stability_attr)] +pub struct MissingStabilityAttr<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_missing_const_stab_attr)] +pub struct MissingConstStabAttr<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes_trait_impl_const_stable)] +#[note] +pub struct TraitImplConstStable { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_feature_only_on_nightly, code = "E0554")] +pub struct FeatureOnlyOnNightly { + #[primary_span] + pub span: Span, + pub release_channel: &'static str, +} + +#[derive(Diagnostic)] +#[diag(passes_unknown_feature, code = "E0635")] +pub struct UnknownFeature { + #[primary_span] + pub span: Span, + pub feature: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_implied_feature_not_exist)] +pub struct ImpliedFeatureNotExist { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub implied_by: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes_duplicate_feature_err, code = "E0636")] +pub struct DuplicateFeatureErr { + #[primary_span] + pub span: Span, + pub feature: Symbol, +} +#[derive(Diagnostic)] +#[diag(passes_missing_const_err)] +pub struct MissingConstErr { + #[primary_span] + #[help] + pub fn_sig_span: Span, + #[label] + pub const_span: Span, +} diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 212ea9e57..88bb39deb 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -1,6 +1,5 @@ use rustc_data_structures::sync::Lock; use rustc_hir as hir; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit; use rustc_hir::{HirId, ItemLocalId}; use rustc_index::bit_set::GrowableBitSet; @@ -42,7 +41,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { struct HirIdValidator<'a, 'hir> { hir_map: Map<'hir>, - owner: Option<LocalDefId>, + owner: Option<hir::OwnerId>, hir_ids_seen: GrowableBitSet<ItemLocalId>, errors: &'a Lock<Vec<String>>, } @@ -63,12 +62,12 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { self.errors.lock().push(f()); } - fn check<F: FnOnce(&mut HirIdValidator<'a, 'hir>)>(&mut self, owner: LocalDefId, walk: F) { + fn check<F: FnOnce(&mut HirIdValidator<'a, 'hir>)>(&mut self, owner: hir::OwnerId, walk: F) { assert!(self.owner.is_none()); self.owner = Some(owner); walk(self); - if owner == CRATE_DEF_ID { + if owner == hir::CRATE_OWNER_ID { return; } @@ -97,14 +96,14 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { missing_items.push(format!( "[local_id: {}, owner: {}]", local_id, - self.hir_map.def_path(owner).to_string_no_crate_verbose() + self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose() )); } self.error(|| { format!( "ItemLocalIds not assigned densely in {}. \ - Max ItemLocalId = {}, missing IDs = {:?}; seens IDs = {:?}", - self.hir_map.def_path(owner).to_string_no_crate_verbose(), + Max ItemLocalId = {}, missing IDs = {:#?}; seens IDs = {:#?}", + self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose(), max, missing_items, self.hir_ids_seen @@ -127,7 +126,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { fn visit_item(&mut self, i: &'hir hir::Item<'hir>) { let mut inner_visitor = self.new_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i)); + inner_visitor.check(i.owner_id, |this| intravisit::walk_item(this, i)); } fn visit_id(&mut self, hir_id: HirId) { @@ -138,8 +137,8 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { format!( "HirIdValidator: The recorded owner of {} is {} instead of {}", self.hir_map.node_to_string(hir_id), - self.hir_map.def_path(hir_id.owner).to_string_no_crate_verbose(), - self.hir_map.def_path(owner).to_string_no_crate_verbose() + self.hir_map.def_path(hir_id.owner.def_id).to_string_no_crate_verbose(), + self.hir_map.def_path(owner.def_id).to_string_no_crate_verbose() ) }); } @@ -149,16 +148,16 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) { let mut inner_visitor = self.new_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i)); + inner_visitor.check(i.owner_id, |this| intravisit::walk_foreign_item(this, i)); } fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) { let mut inner_visitor = self.new_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i)); + inner_visitor.check(i.owner_id, |this| intravisit::walk_trait_item(this, i)); } fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) { let mut inner_visitor = self.new_visitor(self.hir_map); - inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i)); + inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i)); } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a3be827a7..33220fd2b 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -21,75 +21,169 @@ enum Id { None, } -struct NodeData { +struct NodeStats { count: usize, size: usize, } +impl NodeStats { + fn new() -> NodeStats { + NodeStats { count: 0, size: 0 } + } +} + +struct Node { + stats: NodeStats, + subnodes: FxHashMap<&'static str, NodeStats>, +} + +impl Node { + fn new() -> Node { + Node { stats: NodeStats::new(), subnodes: FxHashMap::default() } + } +} + +/// This type measures the size of AST and HIR nodes, by implementing the AST +/// and HIR `Visitor` traits. But we don't measure every visited type because +/// that could cause double counting. +/// +/// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always +/// stored inline within other AST nodes, so we don't implement `visit_ident` +/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is +/// always stored as `P<ast::Expr>`, and every such expression should be +/// measured separately. +/// +/// In general, a `visit_foo` method should be implemented here if the +/// corresponding `Foo` type is always stored on its own, e.g.: `P<Foo>`, +/// `Box<Foo>`, `Vec<Foo>`, `Box<[Foo]>`. +/// +/// There are some types in the AST and HIR tree that the visitors do not have +/// a `visit_*` method for, and so we cannot measure these, which is +/// unfortunate. struct StatCollector<'k> { krate: Option<Map<'k>>, - data: FxHashMap<&'static str, NodeData>, + nodes: FxHashMap<&'static str, Node>, seen: FxHashSet<Id>, } pub fn print_hir_stats(tcx: TyCtxt<'_>) { let mut collector = StatCollector { krate: Some(tcx.hir()), - data: FxHashMap::default(), + nodes: FxHashMap::default(), seen: FxHashSet::default(), }; tcx.hir().walk_toplevel_module(&mut collector); tcx.hir().walk_attributes(&mut collector); - collector.print("HIR STATS"); + collector.print("HIR STATS", "hir-stats"); } -pub fn print_ast_stats(krate: &ast::Crate, title: &str) { +pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) { + use rustc_ast::visit::Visitor; + let mut collector = - StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() }; - ast_visit::walk_crate(&mut collector, krate); - collector.print(title); + StatCollector { krate: None, nodes: FxHashMap::default(), seen: FxHashSet::default() }; + collector.visit_crate(krate); + collector.print(title, prefix); } impl<'k> StatCollector<'k> { - fn record<T>(&mut self, label: &'static str, id: Id, node: &T) { + // Record a top-level node. + fn record<T>(&mut self, label: &'static str, id: Id, val: &T) { + self.record_inner(label, None, id, val); + } + + // Record a two-level entry, with a top-level enum type and a variant. + fn record_variant<T>(&mut self, label1: &'static str, label2: &'static str, id: Id, val: &T) { + self.record_inner(label1, Some(label2), id, val); + } + + fn record_inner<T>( + &mut self, + label1: &'static str, + label2: Option<&'static str>, + id: Id, + val: &T, + ) { if id != Id::None && !self.seen.insert(id) { return; } - let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 }); + let node = self.nodes.entry(label1).or_insert(Node::new()); + node.stats.count += 1; + node.stats.size = std::mem::size_of_val(val); - entry.count += 1; - entry.size = std::mem::size_of_val(node); + if let Some(label2) = label2 { + let subnode = node.subnodes.entry(label2).or_insert(NodeStats::new()); + subnode.count += 1; + subnode.size = std::mem::size_of_val(val); + } } - fn print(&self, title: &str) { - let mut stats: Vec<_> = self.data.iter().collect(); - - stats.sort_by_key(|&(_, ref d)| d.count * d.size); + fn print(&self, title: &str, prefix: &str) { + let mut nodes: Vec<_> = self.nodes.iter().collect(); + nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size); - let mut total_size = 0; + let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); - eprintln!("\n{}\n", title); + eprintln!("{} {}", prefix, title); + eprintln!( + "{} {:<18}{:>18}{:>14}{:>14}", + prefix, "Name", "Accumulated Size", "Count", "Item Size" + ); + eprintln!("{} ----------------------------------------------------------------", prefix); - eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size"); - eprintln!("----------------------------------------------------------------"); + let percent = |m, n| (m * 100) as f64 / n as f64; - for (label, data) in stats { + for (label, node) in nodes { + let size = node.stats.count * node.stats.size; eprintln!( - "{:<18}{:>18}{:>14}{:>14}", + "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}", + prefix, label, - to_readable_str(data.count * data.size), - to_readable_str(data.count), - to_readable_str(data.size) + to_readable_str(size), + percent(size, total_size), + to_readable_str(node.stats.count), + to_readable_str(node.stats.size) ); - - total_size += data.count * data.size; + if !node.subnodes.is_empty() { + let mut subnodes: Vec<_> = node.subnodes.iter().collect(); + subnodes.sort_by_key(|&(_, ref subnode)| subnode.count * subnode.size); + + for (label, subnode) in subnodes { + let size = subnode.count * subnode.size; + eprintln!( + "{} - {:<18}{:>10} ({:4.1}%){:>14}", + prefix, + label, + to_readable_str(size), + percent(size, total_size), + to_readable_str(subnode.count), + ); + } + } } - eprintln!("----------------------------------------------------------------"); - eprintln!("{:<18}{:>18}\n", "Total", to_readable_str(total_size)); + eprintln!("{} ----------------------------------------------------------------", prefix); + eprintln!("{} {:<18}{:>10}", prefix, "Total", to_readable_str(total_size)); + eprintln!("{}", prefix); } } +// Used to avoid boilerplate for types with many variants. +macro_rules! record_variants { + ( + ($self:ident, $val:expr, $kind:expr, $id:expr, $mod:ident, $ty:ty, $tykind:ident), + [$($variant:ident),*] + ) => { + match $kind { + $( + $mod::$tykind::$variant { .. } => { + $self.record_variant(stringify!($ty), stringify!($variant), $id, $val) + } + )* + } + }; +} + impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { fn visit_param(&mut self, param: &'v hir::Param<'v>) { self.record("Param", Id::Node(param.hir_id), param); @@ -122,12 +216,46 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_item(&mut self, i: &'v hir::Item<'v>) { - self.record("Item", Id::Node(i.hir_id()), i); + record_variants!( + (self, i, i.kind, Id::Node(i.hir_id()), hir, Item, ItemKind), + [ + ExternCrate, + Use, + Static, + Const, + Fn, + Macro, + Mod, + ForeignMod, + GlobalAsm, + TyAlias, + OpaqueTy, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl + ] + ); hir_visit::walk_item(self, i) } + fn visit_body(&mut self, b: &'v hir::Body<'v>) { + self.record("Body", Id::None, b); + hir_visit::walk_body(self, b); + } + + fn visit_mod(&mut self, m: &'v hir::Mod<'v>, _s: Span, n: HirId) { + self.record("Mod", Id::None, m); + hir_visit::walk_mod(self, m, n) + } + fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) { - self.record("ForeignItem", Id::Node(i.hir_id()), i); + record_variants!( + (self, i, i.kind, Id::Node(i.hir_id()), hir, ForeignItem, ForeignItemKind), + [Fn, Static, Type] + ); hir_visit::walk_foreign_item(self, i) } @@ -142,7 +270,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { - self.record("Stmt", Id::Node(s.hir_id), s); + record_variants!( + (self, s, s.kind, Id::Node(s.hir_id), hir, Stmt, StmtKind), + [Local, Item, Expr, Semi] + ); hir_visit::walk_stmt(self, s) } @@ -152,50 +283,135 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_pat(&mut self, p: &'v hir::Pat<'v>) { - self.record("Pat", Id::Node(p.hir_id), p); + record_variants!( + (self, p, p.kind, Id::Node(p.hir_id), hir, Pat, PatKind), + [Wild, Binding, Struct, TupleStruct, Or, Path, Tuple, Box, Ref, Lit, Range, Slice] + ); hir_visit::walk_pat(self, p) } - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - self.record("Expr", Id::Node(ex.hir_id), ex); - hir_visit::walk_expr(self, ex) + fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) { + self.record("PatField", Id::Node(f.hir_id), f); + hir_visit::walk_pat_field(self, f) + } + + fn visit_expr(&mut self, e: &'v hir::Expr<'v>) { + record_variants!( + (self, e, e.kind, Id::Node(e.hir_id), hir, Expr, ExprKind), + [ + Box, ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, + DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index, + Path, AddrOf, Break, Continue, Ret, InlineAsm, Struct, Repeat, Yield, Err + ] + ); + hir_visit::walk_expr(self, e) + } + + fn visit_let_expr(&mut self, lex: &'v hir::Let<'v>) { + self.record("Let", Id::Node(lex.hir_id), lex); + hir_visit::walk_let_expr(self, lex) + } + + fn visit_expr_field(&mut self, f: &'v hir::ExprField<'v>) { + self.record("ExprField", Id::Node(f.hir_id), f); + hir_visit::walk_expr_field(self, f) } fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - self.record("Ty", Id::Node(t.hir_id), t); + record_variants!( + (self, t, t.kind, Id::Node(t.hir_id), hir, Ty, TyKind), + [ + Slice, + Array, + Ptr, + Rptr, + BareFn, + Never, + Tup, + Path, + OpaqueDef, + TraitObject, + Typeof, + Infer, + Err + ] + ); hir_visit::walk_ty(self, t) } + fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { + self.record("GenericParam", Id::Node(p.hir_id), p); + hir_visit::walk_generic_param(self, p) + } + + fn visit_generics(&mut self, g: &'v hir::Generics<'v>) { + self.record("Generics", Id::None, g); + hir_visit::walk_generics(self, g) + } + + fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) { + record_variants!( + (self, p, p, Id::None, hir, WherePredicate, WherePredicate), + [BoundPredicate, RegionPredicate, EqPredicate] + ); + hir_visit::walk_where_predicate(self, p) + } + fn visit_fn( &mut self, fk: hir_visit::FnKind<'v>, fd: &'v hir::FnDecl<'v>, b: hir::BodyId, - s: Span, + _: Span, id: hir::HirId, ) { self.record("FnDecl", Id::None, fd); - hir_visit::walk_fn(self, fk, fd, b, s, id) + hir_visit::walk_fn(self, fk, fd, b, id) } - fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate<'v>) { - self.record("WherePredicate", Id::None, predicate); - hir_visit::walk_where_predicate(self, predicate) + fn visit_use(&mut self, p: &'v hir::Path<'v>, hir_id: hir::HirId) { + // This is `visit_use`, but the type is `Path` so record it that way. + self.record("Path", Id::None, p); + hir_visit::walk_use(self, p, hir_id) } fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) { - self.record("TraitItem", Id::Node(ti.hir_id()), ti); + record_variants!( + (self, ti, ti.kind, Id::Node(ti.hir_id()), hir, TraitItem, TraitItemKind), + [Const, Fn, Type] + ); hir_visit::walk_trait_item(self, ti) } + fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) { + self.record("TraitItemRef", Id::Node(ti.id.hir_id()), ti); + hir_visit::walk_trait_item_ref(self, ti) + } + fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { - self.record("ImplItem", Id::Node(ii.hir_id()), ii); + record_variants!( + (self, ii, ii.kind, Id::Node(ii.hir_id()), hir, ImplItem, ImplItemKind), + [Const, Fn, Type] + ); hir_visit::walk_impl_item(self, ii) } - fn visit_param_bound(&mut self, bounds: &'v hir::GenericBound<'v>) { - self.record("GenericBound", Id::None, bounds); - hir_visit::walk_param_bound(self, bounds) + fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) { + self.record("ForeignItemRef", Id::Node(fi.id.hir_id()), fi); + hir_visit::walk_foreign_item_ref(self, fi) + } + + fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) { + self.record("ImplItemRef", Id::Node(ii.id.hir_id()), ii); + hir_visit::walk_impl_item_ref(self, ii) + } + + fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) { + record_variants!( + (self, b, b, Id::None, hir, GenericBound, GenericBound), + [Trait, LangItemTrait, Outlives] + ); + hir_visit::walk_param_bound(self, b) } fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) { @@ -203,14 +419,22 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_field_def(self, s) } - fn visit_variant( - &mut self, - v: &'v hir::Variant<'v>, - g: &'v hir::Generics<'v>, - item_id: hir::HirId, - ) { + fn visit_variant(&mut self, v: &'v hir::Variant<'v>) { self.record("Variant", Id::None, v); - hir_visit::walk_variant(self, v, g, item_id) + hir_visit::walk_variant(self, v) + } + + fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) { + record_variants!( + (self, ga, ga, Id::Node(ga.hir_id()), hir, GenericArg, GenericArg), + [Lifetime, Type, Const, Infer] + ); + match ga { + hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + hir::GenericArg::Type(ty) => self.visit_ty(ty), + hir::GenericArg::Const(ct) => self.visit_anon_const(&ct.value), + hir::GenericArg::Infer(inf) => self.visit_infer(inf), + } } fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { @@ -218,19 +442,19 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_lifetime(self, lifetime) } - fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) { - self.record("QPath", Id::None, qpath); - hir_visit::walk_qpath(self, qpath, id, span) - } - fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { self.record("Path", Id::None, path); hir_visit::walk_path(self, path) } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) { + fn visit_path_segment(&mut self, path_segment: &'v hir::PathSegment<'v>) { self.record("PathSegment", Id::None, path_segment); - hir_visit::walk_path_segment(self, path_span, path_segment) + hir_visit::walk_path_segment(self, path_segment) + } + + fn visit_generic_args(&mut self, ga: &'v hir::GenericArgs<'v>) { + self.record("GenericArgs", Id::None, ga); + hir_visit::walk_generic_args(self, ga) } fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) { @@ -241,16 +465,45 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { fn visit_attribute(&mut self, attr: &'v ast::Attribute) { self.record("Attribute", Id::Attr(attr.id), attr); } + + fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) { + self.record("InlineAsm", Id::None, asm); + hir_visit::walk_inline_asm(self, asm, id); + } } impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { - self.record("ForeignItem", Id::None, i); + record_variants!( + (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind), + [Static, Fn, TyAlias, MacCall] + ); ast_visit::walk_foreign_item(self, i) } fn visit_item(&mut self, i: &'v ast::Item) { - self.record("Item", Id::None, i); + record_variants!( + (self, i, i.kind, Id::None, ast, Item, ItemKind), + [ + ExternCrate, + Use, + Static, + Const, + Fn, + Mod, + ForeignMod, + GlobalAsm, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + MacCall, + MacroDef + ] + ); ast_visit::walk_item(self, i) } @@ -265,47 +518,119 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_stmt(&mut self, s: &'v ast::Stmt) { - self.record("Stmt", Id::None, s); + record_variants!( + (self, s, s.kind, Id::None, ast, Stmt, StmtKind), + [Local, Item, Expr, Semi, Empty, MacCall] + ); ast_visit::walk_stmt(self, s) } + fn visit_param(&mut self, p: &'v ast::Param) { + self.record("Param", Id::None, p); + ast_visit::walk_param(self, p) + } + fn visit_arm(&mut self, a: &'v ast::Arm) { self.record("Arm", Id::None, a); ast_visit::walk_arm(self, a) } fn visit_pat(&mut self, p: &'v ast::Pat) { - self.record("Pat", Id::None, p); + record_variants!( + (self, p, p.kind, Id::None, ast, Pat, PatKind), + [ + Wild, + Ident, + Struct, + TupleStruct, + Or, + Path, + Tuple, + Box, + Ref, + Lit, + Range, + Slice, + Rest, + Paren, + MacCall + ] + ); ast_visit::walk_pat(self, p) } - fn visit_expr(&mut self, ex: &'v ast::Expr) { - self.record("Expr", Id::None, ex); - ast_visit::walk_expr(self, ex) + fn visit_expr(&mut self, e: &'v ast::Expr) { + record_variants!( + (self, e, e.kind, Id::None, ast, Expr, ExprKind), + [ + Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, + If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign, + AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, + InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err + ] + ); + ast_visit::walk_expr(self, e) } fn visit_ty(&mut self, t: &'v ast::Ty) { - self.record("Ty", Id::None, t); + record_variants!( + (self, t, t.kind, Id::None, ast, Ty, TyKind), + [ + Slice, + Array, + Ptr, + Rptr, + BareFn, + Never, + Tup, + Path, + TraitObject, + ImplTrait, + Paren, + Typeof, + Infer, + ImplicitSelf, + MacCall, + Err, + CVarArgs + ] + ); + ast_visit::walk_ty(self, t) } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) { + fn visit_generic_param(&mut self, g: &'v ast::GenericParam) { + self.record("GenericParam", Id::None, g); + ast_visit::walk_generic_param(self, g) + } + + fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { + record_variants!( + (self, p, p, Id::None, ast, WherePredicate, WherePredicate), + [BoundPredicate, RegionPredicate, EqPredicate] + ); + ast_visit::walk_where_predicate(self, p) + } + + fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) { self.record("FnDecl", Id::None, fk.decl()); - ast_visit::walk_fn(self, fk, s) + ast_visit::walk_fn(self, fk) } - fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - let label = match ctxt { - ast_visit::AssocCtxt::Trait => "TraitItem", - ast_visit::AssocCtxt::Impl => "ImplItem", - }; - self.record(label, Id::None, item); - ast_visit::walk_assoc_item(self, item, ctxt); + fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + record_variants!( + (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind), + [Const, Fn, Type, MacCall] + ); + ast_visit::walk_assoc_item(self, i, ctxt); } - fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound, _ctxt: BoundKind) { - self.record("GenericBound", Id::None, bounds); - ast_visit::walk_param_bound(self, bounds) + fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) { + record_variants!( + (self, b, b, Id::None, ast, GenericBound, GenericBound), + [Trait, Outlives] + ); + ast_visit::walk_param_bound(self, b) } fn visit_field_def(&mut self, s: &'v ast::FieldDef) { @@ -318,27 +643,52 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) { - self.record("Lifetime", Id::None, lifetime); - ast_visit::walk_lifetime(self, lifetime) + // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one + // non-inline use (in `ast::UseTreeKind::Nested). The former case is more + // common, so we don't implement `visit_use_tree` and tolerate the missed + // coverage in the latter case. + + // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and + // one non-inline use (in `ast::Path::segments`). The latter case is more + // common than the former case, so we implement this visitor and tolerate + // the double counting in the former case. + fn visit_path_segment(&mut self, path_segment: &'v ast::PathSegment) { + self.record("PathSegment", Id::None, path_segment); + ast_visit::walk_path_segment(self, path_segment) } - fn visit_mac_call(&mut self, mac: &'v ast::MacCall) { - self.record("MacCall", Id::None, mac); - ast_visit::walk_mac(self, mac) + // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one + // non-inline use (in `ast::PathSegment::args`). The latter case is more + // common, so we implement `visit_generic_args` and tolerate the double + // counting in the former case. + fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) { + record_variants!( + (self, g, g, Id::None, ast, GenericArgs, GenericArgs), + [AngleBracketed, Parenthesized] + ); + ast_visit::walk_generic_args(self, g) } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) { - self.record("PathSegment", Id::None, path_segment); - ast_visit::walk_path_segment(self, path_span, path_segment) + fn visit_attribute(&mut self, attr: &'v ast::Attribute) { + record_variants!( + (self, attr, attr.kind, Id::None, ast, Attribute, AttrKind), + [Normal, DocComment] + ); + ast_visit::walk_attribute(self, attr) } - fn visit_assoc_constraint(&mut self, constraint: &'v ast::AssocConstraint) { - self.record("AssocConstraint", Id::None, constraint); - ast_visit::walk_assoc_constraint(self, constraint) + fn visit_expr_field(&mut self, f: &'v ast::ExprField) { + self.record("ExprField", Id::None, f); + ast_visit::walk_expr_field(self, f) } - fn visit_attribute(&mut self, attr: &'v ast::Attribute) { - self.record("Attribute", Id::None, attr); + fn visit_crate(&mut self, krate: &'v ast::Crate) { + self.record("Crate", Id::None, krate); + ast_visit::walk_crate(self, krate) + } + + fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) { + self.record("InlineAsm", Id::None, asm); + ast_visit::walk_inline_asm(self, asm) } } diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 79900a90a..df811be2a 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -8,9 +8,11 @@ //! * Functions called by the compiler itself. use crate::check_attr::target_from_impl_item; +use crate::errors::{ + DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem, +}; use crate::weak_lang_items; -use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -18,10 +20,16 @@ use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS}; use rustc_hir::{HirId, LangItem, LanguageItems, Target}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ExternCrate; -use rustc_span::Span; +use rustc_span::{symbol::kw::Empty, Span}; use rustc_middle::ty::query::Providers; +pub(crate) enum Duplicate { + Plain, + Crate, + CrateDepends, +} + struct LanguageItemCollector<'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -34,42 +42,24 @@ impl<'tcx> LanguageItemCollector<'tcx> { fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { let attrs = self.tcx.hir().attrs(hir_id); - if let Some((value, span)) = extract(&attrs) { - match ITEM_REFS.get(&value).cloned() { + if let Some((name, span)) = extract(&attrs) { + match ITEM_REFS.get(&name).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { self.collect_item_extended(item_index, hir_id, span); } // Known lang item with attribute on incorrect target. Some((_, expected_target)) => { - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(LangItemOnIncorrectTarget { span, - E0718, - "`{}` language item must be applied to a {}", - value, + name, expected_target, - ) - .span_label( - span, - format!( - "attribute should be applied to a {}, not a {}", - expected_target, actual_target, - ), - ) - .emit(); + actual_target, + }); } // Unknown lang item. _ => { - struct_span_err!( - self.tcx.sess, - span, - E0522, - "definition of an unknown language item: `{}`", - value - ) - .span_label(span, format!("definition of unknown language item `{}`", value)) - .emit(); + self.tcx.sess.emit_err(UnknownLangItem { span, name }); } } } @@ -79,74 +69,72 @@ impl<'tcx> LanguageItemCollector<'tcx> { // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { if original_def_id != item_def_id { - let lang_item = LangItem::from_u32(item_index as u32).unwrap(); - let name = lang_item.name(); - let mut err = match self.tcx.hir().span_if_local(item_def_id) { - Some(span) => struct_span_err!( - self.tcx.sess, - span, - E0152, - "found duplicate lang item `{}`", - name - ), - None => match self.tcx.extern_crate(item_def_id) { - Some(ExternCrate { dependency_of, .. }) => { - self.tcx.sess.struct_err(&format!( - "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.", - self.tcx.crate_name(item_def_id.krate), - self.tcx.crate_name(*dependency_of), - name - )) - } - _ => self.tcx.sess.struct_err(&format!( - "duplicate lang item in crate `{}`: `{}`.", - self.tcx.crate_name(item_def_id.krate), - name - )), - }, + let local_span = self.tcx.hir().span_if_local(item_def_id); + let lang_item_name = LangItem::from_u32(item_index as u32).unwrap().name(); + let crate_name = self.tcx.crate_name(item_def_id.krate); + let mut dependency_of = Empty; + let is_local = item_def_id.is_local(); + let path = if is_local { + String::new() + } else { + self.tcx + .crate_extern_paths(item_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::<Vec<_>>() + .join(", ") + .into() }; - if let Some(span) = self.tcx.hir().span_if_local(original_def_id) { - err.span_note(span, "the lang item is first defined here"); + let first_defined_span = self.tcx.hir().span_if_local(original_def_id); + let mut orig_crate_name = Empty; + let mut orig_dependency_of = Empty; + let orig_is_local = original_def_id.is_local(); + let orig_path = if orig_is_local { + String::new() } else { - match self.tcx.extern_crate(original_def_id) { - Some(ExternCrate { dependency_of, .. }) => { - err.note(&format!( - "the lang item is first defined in crate `{}` (which `{}` depends on)", - self.tcx.crate_name(original_def_id.krate), - self.tcx.crate_name(*dependency_of) - )); - } - _ => { - err.note(&format!( - "the lang item is first defined in crate `{}`.", - self.tcx.crate_name(original_def_id.krate) - )); - } + self.tcx + .crate_extern_paths(original_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::<Vec<_>>() + .join(", ") + .into() + }; + if first_defined_span.is_none() { + orig_crate_name = self.tcx.crate_name(original_def_id.krate); + if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = + self.tcx.extern_crate(original_def_id) + { + orig_dependency_of = self.tcx.crate_name(*inner_dependency_of); } - let mut note_def = |which, def_id: DefId| { - let crate_name = self.tcx.crate_name(def_id.krate); - let note = if def_id.is_local() { - format!("{} definition in the local crate (`{}`)", which, crate_name) - } else { - let paths: Vec<_> = self - .tcx - .crate_extern_paths(def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect(); - format!( - "{} definition in `{}` loaded from {}", - which, - crate_name, - paths.join(", ") - ) - }; - err.note(¬e); - }; - note_def("first", original_def_id); - note_def("second", item_def_id); } - err.emit(); + + let duplicate = if local_span.is_some() { + Duplicate::Plain + } else { + match self.tcx.extern_crate(item_def_id) { + Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { + dependency_of = self.tcx.crate_name(*inner_dependency_of); + Duplicate::CrateDepends + } + _ => Duplicate::Crate, + } + }; + + self.tcx.sess.emit_err(DuplicateLangItem { + local_span, + lang_item_name, + crate_name, + dependency_of, + is_local, + path, + first_defined_span, + orig_crate_name, + orig_dependency_of, + orig_is_local, + orig_path, + duplicate, + }); } } @@ -179,41 +167,30 @@ impl<'tcx> LanguageItemCollector<'tcx> { None => (0, *item_span), }; + let mut at_least = false; let required = match lang_item.required_generics() { - GenericRequirement::Exact(num) if num != actual_num => { - Some((format!("{}", num), pluralize!(num))) - } + GenericRequirement::Exact(num) if num != actual_num => Some(num), GenericRequirement::Minimum(num) if actual_num < num => { - Some((format!("at least {}", num), pluralize!(num))) - } + at_least = true; + Some(num)} + , // If the number matches, or there is no requirement, handle it normally _ => None, }; - if let Some((range_str, pluralized)) = required { + if let Some(num) = required { // We are issuing E0718 "incorrect target" here, because while the // item kind of the target is correct, the target is still wrong // because of the wrong number of generic arguments. - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(IncorrectTarget { span, - E0718, - "`{}` language item must be applied to a {} with {} generic argument{}", - name, - kind.descr(), - range_str, - pluralized, - ) - .span_label( generics_span, - format!( - "this {} has {} generic argument{}", - kind.descr(), - actual_num, - pluralize!(actual_num), - ), - ) - .emit(); + name: name.as_str(), + kind: kind.descr(), + num, + actual_num, + at_least, + }); // return early to not collect the lang item return; @@ -240,9 +217,9 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { let crate_items = tcx.hir_crate_items(()); for id in crate_items.items() { - collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.def_id)), id.hir_id()); + collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.owner_id)), id.hir_id()); - if matches!(tcx.def_kind(id.def_id), DefKind::Enum) { + if matches!(tcx.def_kind(id.owner_id), DefKind::Enum) { let item = tcx.hir().item(id); if let hir::ItemKind::Enum(def, ..) = &item.kind { for variant in def.variants { diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index fd03f6571..5322baee7 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -3,20 +3,23 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; +use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField}; + pub fn test_layout(tcx: TyCtxt<'_>) { if tcx.features().rustc_attrs { // if the `rustc_attrs` feature is not enabled, don't bother testing layout for id in tcx.hir().items() { if matches!( - tcx.def_kind(id.def_id), + tcx.def_kind(id.owner_id), DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union ) { - for attr in tcx.get_attrs(id.def_id.to_def_id(), sym::rustc_layout) { - dump_layout_of(tcx, id.def_id, attr); + for attr in tcx.get_attrs(id.owner_id.to_def_id(), sym::rustc_layout) { + dump_layout_of(tcx, id.owner_id.def_id, attr); } } } @@ -35,62 +38,64 @@ fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attri for meta_item in meta_items { match meta_item.name_or_empty() { sym::abi => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("abi: {:?}", ty_layout.abi), - ); + tcx.sess.emit_err(Abi { + span: tcx.def_span(item_def_id.to_def_id()), + abi: format!("{:?}", ty_layout.abi), + }); } sym::align => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("align: {:?}", ty_layout.align), - ); + tcx.sess.emit_err(Align { + span: tcx.def_span(item_def_id.to_def_id()), + align: format!("{:?}", ty_layout.align), + }); } sym::size => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("size: {:?}", ty_layout.size), - ); + tcx.sess.emit_err(Size { + span: tcx.def_span(item_def_id.to_def_id()), + size: format!("{:?}", ty_layout.size), + }); } sym::homogeneous_aggregate => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!( - "homogeneous_aggregate: {:?}", - ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), + tcx.sess.emit_err(HomogeneousAggregate { + span: tcx.def_span(item_def_id.to_def_id()), + homogeneous_aggregate: format!( + "{:?}", + ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }) ), - ); + }); } sym::debug => { - let normalized_ty = tcx.normalize_erasing_regions( - param_env.with_reveal_all_normalized(tcx), - ty, - ); - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), + let normalized_ty = format!( + "{:?}", + tcx.normalize_erasing_regions( + param_env.with_reveal_all_normalized(tcx), + ty, + ) ); + let ty_layout = format!("{:#?}", *ty_layout); + tcx.sess.emit_err(LayoutOf { + span: tcx.def_span(item_def_id.to_def_id()), + normalized_ty, + ty_layout, + }); } name => { - tcx.sess.span_err( - meta_item.span(), - &format!("unrecognized field name `{}`", name), - ); + tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name }); } } } } Err(layout_error) => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("layout error: {:?}", layout_error), - ); + tcx.sess.emit_fatal(Spanned { + node: layout_error, + span: tcx.def_span(item_def_id.to_def_id()), + }); } } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 7b2f83958..15f60f626 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -5,10 +5,11 @@ //! This API is completely unstable and subject to change. #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(try_blocks)] diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index e05994f13..b5843c0ae 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -5,7 +5,7 @@ //! collect them instead. use rustc_ast::{Attribute, MetaItemKind}; -use rustc_errors::struct_span_err; +use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER}; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::LibFeatures; @@ -14,6 +14,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; +use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice}; + fn new_lib_features() -> LibFeatures { LibFeatures { stable: Default::default(), unstable: Default::default() } } @@ -29,11 +31,16 @@ impl<'tcx> LibFeatureCollector<'tcx> { } fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> { - let stab_attrs = - [sym::stable, sym::unstable, sym::rustc_const_stable, sym::rustc_const_unstable]; + let stab_attrs = [ + sym::stable, + sym::unstable, + sym::rustc_const_stable, + sym::rustc_const_unstable, + sym::rustc_default_body_unstable, + ]; // Find a stability attribute: one of #[stable(…)], #[unstable(…)], - // #[rustc_const_stable(…)], or #[rustc_const_unstable(…)]. + // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable]. if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) { let meta_kind = attr.meta_kind(); if let Some(MetaItemKind::List(ref metas)) = meta_kind { @@ -49,12 +56,21 @@ impl<'tcx> LibFeatureCollector<'tcx> { } } } + + if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER { + since = Some(rust_version_symbol()); + } + if let Some(feature) = feature { // This additional check for stability is to make sure we // don't emit additional, irrelevant errors for malformed // attributes. - let is_unstable = - matches!(*stab_attr, sym::unstable | sym::rustc_const_unstable); + let is_unstable = matches!( + *stab_attr, + sym::unstable + | sym::rustc_const_unstable + | sym::rustc_default_body_unstable + ); if since.is_some() || is_unstable { return Some((feature, since, attr.span)); } @@ -77,14 +93,12 @@ impl<'tcx> LibFeatureCollector<'tcx> { (Some(since), _, false) => { if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { if *prev_since != since { - self.span_feature_error( + self.tcx.sess.emit_err(FeatureStableTwice { span, - &format!( - "feature `{}` is declared stable since {}, \ - but was previously declared stable since {}", - feature, since, prev_since, - ), - ); + feature, + since, + prev_since: *prev_since, + }); return; } } @@ -95,22 +109,17 @@ impl<'tcx> LibFeatureCollector<'tcx> { self.lib_features.unstable.insert(feature, span); } (Some(_), _, true) | (None, true, _) => { - self.span_feature_error( + let declared = if since.is_some() { "stable" } else { "unstable" }; + let prev_declared = if since.is_none() { "stable" } else { "unstable" }; + self.tcx.sess.emit_err(FeaturePreviouslyDeclared { span, - &format!( - "feature `{}` is declared {}, but was previously declared {}", - feature, - if since.is_some() { "stable" } else { "unstable" }, - if since.is_none() { "stable" } else { "unstable" }, - ), - ); + feature, + declared, + prev_declared, + }); } } } - - fn span_feature_error(&self, span: Span, msg: &str) { - struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit(); - } } impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 461dd52b9..c6fe40f72 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -89,16 +89,15 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::*; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::vec::IndexVec; -use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; +use rustc_span::{BytePos, Span}; use std::collections::VecDeque; use std::io; @@ -139,12 +138,54 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { } } -fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx)); +fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) { + let local_def_id = match def_id.as_local() { + None => return, + Some(def_id) => def_id, + }; + + // Don't run unused pass for #[derive()] + let parent = tcx.local_parent(local_def_id); + if let DefKind::Impl = tcx.def_kind(parent) + && tcx.has_attr(parent.to_def_id(), sym::automatically_derived) + { + return; + } + + // Don't run unused pass for #[naked] + if tcx.has_attr(def_id, sym::naked) { + return; + } + + let mut maps = IrMaps::new(tcx); + let body_id = tcx.hir().body_owned_by(local_def_id); + let hir_id = tcx.hir().body_owner(body_id); + let body = tcx.hir().body(body_id); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for &var_hir_id in upvars.keys() { + let var_name = tcx.hir().name(var_hir_id); + maps.add_variable(Upvar(var_hir_id, var_name)); + } + } + + // gather up the various local variables, significant expressions, + // and so forth: + maps.visit_body(body); + + // compute liveness + let mut lsets = Liveness::new(&mut maps, local_def_id); + let entry_ln = lsets.compute(&body, hir_id); + lsets.log_liveness(entry_ln, body_id.hir_id); + + // check for various error conditions + lsets.visit_body(body); + lsets.warn_about_unused_upvars(entry_ln); + lsets.warn_about_unused_args(body, entry_ln); } pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_liveness, ..*providers }; + *providers = Providers { check_liveness, ..*providers }; } // ______________________________________________________________________ @@ -188,6 +229,19 @@ enum VarKind { Upvar(HirId, Symbol), } +struct CollectLitsVisitor<'tcx> { + lit_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Lit(_) = expr.kind { + self.lit_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + struct IrMaps<'tcx> { tcx: TyCtxt<'tcx>, live_node_map: HirIdMap<LiveNode>, @@ -316,56 +370,6 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - debug!("visit_body {:?}", body.id()); - - // swap in a new set of IR maps for this body - let mut maps = IrMaps::new(self.tcx); - let hir_id = maps.tcx.hir().body_owner(body.id()); - let local_def_id = maps.tcx.hir().local_def_id(hir_id); - let def_id = local_def_id.to_def_id(); - - // Don't run unused pass for #[derive()] - let parent = self.tcx.local_parent(local_def_id); - if let DefKind::Impl = self.tcx.def_kind(parent) - && self.tcx.has_attr(parent.to_def_id(), sym::automatically_derived) - { - return; - } - - // Don't run unused pass for #[naked] - if self.tcx.has_attr(def_id, sym::naked) { - return; - } - - if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { - for &var_hir_id in upvars.keys() { - let var_name = maps.tcx.hir().name(var_hir_id); - maps.add_variable(Upvar(var_hir_id, var_name)); - } - } - - // gather up the various local variables, significant expressions, - // and so forth: - intravisit::walk_body(&mut maps, body); - - // compute liveness - let mut lsets = Liveness::new(&mut maps, local_def_id); - let entry_ln = lsets.compute(&body, hir_id); - lsets.log_liveness(entry_ln, body.id().hir_id); - - // check for various error conditions - lsets.visit_body(body); - lsets.warn_about_unused_upvars(entry_ln); - lsets.warn_about_unused_args(body, entry_ln); - } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.add_from_pat(&local.pat); if local.els.is_some() { @@ -1035,9 +1039,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&f, succ) } - hir::ExprKind::MethodCall(.., ref args, _) => { + hir::ExprKind::MethodCall(.., receiver, ref args, _) => { let succ = self.check_is_ty_uninhabited(expr, succ); - self.propagate_through_exprs(args, succ) + let succ = self.propagate_through_exprs(args, succ); + self.propagate_through_expr(receiver, succ) } hir::ExprKind::Tup(ref exprs) => self.propagate_through_exprs(exprs, succ), @@ -1314,14 +1319,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // that we do not emit the same warning twice if the uninhabited type // is indeed `!`. + let msg = format!("unreachable {}", descr); self.ir.tcx.struct_span_lint_hir( lint::builtin::UNREACHABLE_CODE, expr_id, expr_span, - |lint| { - let msg = format!("unreachable {}", descr); - lint.build(&msg) - .span_label(expr_span, &msg) + &msg, + |diag| { + diag.span_label(expr_span, &msg) .span_label(orig_span, "any code following this expression is unreachable") .span_note( orig_span, @@ -1330,7 +1335,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { orig_ty ), ) - .emit(); }, ); } @@ -1342,7 +1346,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { - self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| { + self.check_unused_vars_in_pat(&local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); } @@ -1357,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { } fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {}); + self.check_unused_vars_in_pat(&arm.pat, None, None, |_, _, _, _| {}); intravisit::walk_arm(self, arm); } } @@ -1396,7 +1400,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } hir::ExprKind::Let(let_expr) => { - this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {}); + this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {}); } // no correctness conditions related to liveness @@ -1486,14 +1490,8 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_ASSIGNMENTS, var_hir_id, vec![span], - |lint| { - lint.build(&format!( - "value captured by `{}` is never read", - name - )) - .help("did you mean to capture by reference instead?") - .emit(); - }, + format!("value captured by `{}` is never read", name), + |lint| lint.help("did you mean to capture by reference instead?"), ); } } @@ -1503,11 +1501,8 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_VARIABLES, var_hir_id, vec![span], - |lint| { - lint.build(&format!("unused variable: `{}`", name)) - .help("did you mean to capture by reference instead?") - .emit(); - }, + format!("unused variable: `{}`", name), + |lint| lint.help("did you mean to capture by reference instead?"), ); } } @@ -1517,13 +1512,18 @@ impl<'tcx> Liveness<'_, 'tcx> { fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { for p in body.params { - self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| { - if !self.live_on_entry(ln, var) { - self.report_unused_assign(hir_id, spans, var, |name| { - format!("value passed to `{}` is never read", name) - }); - } - }); + self.check_unused_vars_in_pat( + &p.pat, + Some(entry_ln), + Some(body), + |spans, hir_id, ln, var| { + if !self.live_on_entry(ln, var) { + self.report_unused_assign(hir_id, spans, var, |name| { + format!("value passed to `{}` is never read", name) + }); + } + }, + ); } } @@ -1531,6 +1531,7 @@ impl<'tcx> Liveness<'_, 'tcx> { &self, pat: &hir::Pat<'_>, entry_ln: Option<LiveNode>, + opt_body: Option<&hir::Body<'_>>, on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable), ) { // In an or-pattern, only consider the variable; any later patterns must have the same @@ -1549,6 +1550,8 @@ impl<'tcx> Liveness<'_, 'tcx> { .or_insert_with(|| (ln, var, vec![id_and_sp])); }); + let can_remove = matches!(&pat.kind, hir::PatKind::Struct(_, _, true)); + for (_, (ln, var, hir_ids_and_spans)) in vars { if self.used_on_entry(ln, var) { let id = hir_ids_and_spans[0].0; @@ -1556,16 +1559,20 @@ impl<'tcx> Liveness<'_, 'tcx> { hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect(); on_used_on_entry(spans, id, ln, var); } else { - self.report_unused(hir_ids_and_spans, ln, var); + self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body); } } } + #[instrument(skip(self), level = "INFO")] fn report_unused( &self, hir_ids_and_spans: Vec<(HirId, Span, Span)>, ln: LiveNode, var: Variable, + can_remove: bool, + pat: &hir::Pat<'_>, + opt_body: Option<&hir::Body<'_>>, ) { let first_hir_id = hir_ids_and_spans[0].0; @@ -1584,12 +1591,34 @@ impl<'tcx> Liveness<'_, 'tcx> { .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::<Vec<_>>(), + format!("variable `{}` is assigned to, but never used", name), + |lint| lint.note(&format!("consider using `_{}` instead", name)), + ) + } else if can_remove { + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_VARIABLES, + first_hir_id, + hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>(), + format!("unused variable: `{}`", name), |lint| { - lint.build(&format!("variable `{}` is assigned to, but never used", name)) - .note(&format!("consider using `_{}` instead", name)) - .emit(); + lint.multipart_suggestion( + "try removing the field", + hir_ids_and_spans + .iter() + .map(|(_, pat_span, _)| { + let span = self + .ir + .tcx + .sess + .source_map() + .span_extend_to_next_char(*pat_span, ',', true); + (span.with_hi(BytePos(span.hi().0 + 1)), String::new()) + }) + .collect(), + Applicability::MachineApplicable, + ) }, - ) + ); } else { let (shorthands, non_shorthands): (Vec<_>, Vec<_>) = hir_ids_and_spans.iter().copied().partition(|(hir_id, _, ident_span)| { @@ -1618,14 +1647,13 @@ impl<'tcx> Liveness<'_, 'tcx> { .iter() .map(|(_, pat_span, _)| *pat_span) .collect::<Vec<_>>(), + format!("unused variable: `{}`", name), |lint| { - let mut err = lint.build(&format!("unused variable: `{}`", name)); - err.multipart_suggestion( + lint.multipart_suggestion( "try ignoring the field", shorthands, Applicability::MachineApplicable, - ); - err.emit(); + ) }, ); } else { @@ -1641,14 +1669,16 @@ impl<'tcx> Liveness<'_, 'tcx> { .iter() .map(|(_, _, ident_span)| *ident_span) .collect::<Vec<_>>(), + format!("unused variable: `{}`", name), |lint| { - let mut err = lint.build(&format!("unused variable: `{}`", name)); - err.multipart_suggestion( + if self.has_added_lit_match_name_span(&name, opt_body, lint) { + lint.span_label(pat.span, "unused variable"); + } + lint.multipart_suggestion( "if this is intentional, prefix it with an underscore", non_shorthands, Applicability::MachineApplicable, - ); - err.emit(); + ) }, ); } @@ -1656,6 +1686,42 @@ impl<'tcx> Liveness<'_, 'tcx> { } } + fn has_added_lit_match_name_span( + &self, + name: &str, + opt_body: Option<&hir::Body<'_>>, + err: &mut rustc_errors::DiagnosticBuilder<'_, ()>, + ) -> bool { + let mut has_litstring = false; + let Some(opt_body) = opt_body else {return false;}; + let mut visitor = CollectLitsVisitor { lit_exprs: vec![] }; + intravisit::walk_body(&mut visitor, opt_body); + for lit_expr in visitor.lit_exprs { + let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue }; + let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; }; + let name_str: &str = syb.as_str(); + let mut name_pa = String::from("{"); + name_pa.push_str(&name); + name_pa.push('}'); + if name_str.contains(&name_pa) { + err.span_label( + lit_expr.span, + "you might have meant to use string interpolation in this string literal", + ); + err.multipart_suggestion( + "string interpolation only works in `format!` invocations", + vec![ + (lit_expr.span.shrink_to_lo(), "format!(".to_string()), + (lit_expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + has_litstring = true; + } + } + has_litstring + } + fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) { if !self.live_on_exit(ln, var) { self.report_unused_assign(hir_id, spans, var, |name| { @@ -1676,11 +1742,8 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans, - |lint| { - lint.build(&message(&name)) - .help("maybe it is overwritten before being read?") - .emit(); - }, + message(&name), + |lint| lint.help("maybe it is overwritten before being read?"), ) } } diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index cdda0e388..077194ec6 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -1,6 +1,5 @@ use Context::*; -use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -13,6 +12,11 @@ use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::Span; +use crate::errors::{ + BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop, + UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock, +}; + #[derive(Clone, Copy, Debug, PartialEq)] enum Context { Normal, @@ -90,7 +94,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { Ok(loop_id) => Some(loop_id), Err(hir::LoopIdError::OutsideLoopScope) => None, Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.emit_unlabled_cf_in_while_condition(e.span, "break"); + self.sess.emit_err(UnlabeledCfInWhileCondition { + span: e.span, + cf_type: "break", + }); None } Err(hir::LoopIdError::UnresolvedLabel) => None, @@ -116,69 +123,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { match loop_kind { None | Some(hir::LoopSource::Loop) => (), Some(kind) => { - let mut err = struct_span_err!( - self.sess, - e.span, - E0571, - "`break` with value from a `{}` loop", - kind.name() - ); - err.span_label( - e.span, - "can only break with a value inside `loop` or breakable block", + let suggestion = format!( + "break{}", + break_label + .label + .map_or_else(String::new, |l| format!(" {}", l.ident)) ); - if let Some(head) = head { - err.span_label( - head, - &format!( - "you can't `break` with a value in a `{}` loop", - kind.name() - ), - ); - } - err.span_suggestion( - e.span, - &format!( - "use `break` on its own without a value inside this `{}` loop", - kind.name(), - ), - format!( - "break{}", - break_label - .label - .map_or_else(String::new, |l| format!(" {}", l.ident)) - ), - Applicability::MaybeIncorrect, - ); - if let (Some(label), None) = (loop_label, break_label.label) { - match break_expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { - segments: [segment], - res: hir::def::Res::Err, - .. - }, - )) if label.ident.to_string() - == format!("'{}", segment.ident) => - { - // This error is redundant, we will have already emitted a - // suggestion to use the label when `segment` wasn't found - // (hence the `Res::Err` check). - err.delay_as_bug(); - } - _ => { - err.span_suggestion( - break_expr.span, - "alternatively, you might have meant to use the \ - available loop label", - label.ident, - Applicability::MaybeIncorrect, - ); - } - } - } - err.emit(); + self.sess.emit_err(BreakNonLoop { + span: e.span, + head, + kind: kind.name(), + suggestion, + loop_label, + break_label: break_label.label, + break_expr_kind: &break_expr.kind, + break_expr_span: break_expr.span, + }); } } } @@ -191,19 +151,17 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { match destination.target_id { Ok(loop_id) => { if let Node::Block(block) = self.hir_map.find(loop_id).unwrap() { - struct_span_err!( - self.sess, - e.span, - E0696, - "`continue` pointing to a labeled block" - ) - .span_label(e.span, "labeled blocks cannot be `continue`'d") - .span_label(block.span, "labeled block the `continue` points to") - .emit(); + self.sess.emit_err(ContinueLabeledBlock { + span: e.span, + block_span: block.span, + }); } } Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.emit_unlabled_cf_in_while_condition(e.span, "continue"); + self.sess.emit_err(UnlabeledCfInWhileCondition { + span: e.span, + cf_type: "continue", + }); } Err(_) => {} } @@ -226,21 +184,16 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { } fn require_break_cx(&self, name: &str, span: Span) { - let err_inside_of = |article, ty, closure_span| { - struct_span_err!(self.sess, span, E0267, "`{}` inside of {} {}", name, article, ty) - .span_label(span, format!("cannot `{}` inside of {} {}", name, article, ty)) - .span_label(closure_span, &format!("enclosing {}", ty)) - .emit(); - }; - match self.cx { LabeledBlock | Loop(_) => {} - Closure(closure_span) => err_inside_of("a", "closure", closure_span), - AsyncClosure(closure_span) => err_inside_of("an", "`async` block", closure_span), + Closure(closure_span) => { + self.sess.emit_err(BreakInsideClosure { span, closure_span, name }); + } + AsyncClosure(closure_span) => { + self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name }); + } Normal | AnonConst => { - struct_span_err!(self.sess, span, E0268, "`{}` outside of a loop", name) - .span_label(span, format!("cannot `{}` outside of a loop", name)) - .emit(); + self.sess.emit_err(OutsideLoop { span, name }); } } } @@ -251,37 +204,13 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { label: &Destination, cf_type: &str, ) -> bool { - if !span.is_desugaring(DesugaringKind::QuestionMark) && self.cx == LabeledBlock { - if label.label.is_none() { - struct_span_err!( - self.sess, - span, - E0695, - "unlabeled `{}` inside of a labeled block", - cf_type - ) - .span_label( - span, - format!( - "`{}` statements that would diverge to or through \ - a labeled block need to bear a label", - cf_type - ), - ) - .emit(); - return true; - } + if !span.is_desugaring(DesugaringKind::QuestionMark) + && self.cx == LabeledBlock + && label.label.is_none() + { + self.sess.emit_err(UnlabeledInLabeledBlock { span, cf_type }); + return true; } false } - fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) { - struct_span_err!( - self.sess, - span, - E0590, - "`break` or `continue` with no label in the condition of a `while` loop" - ) - .span_label(span, format!("unlabeled `{}` in the condition of a `while` loop", cf_type)) - .emit(); - } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 20765abf3..acc54e7e1 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,11 +1,11 @@ //! Checks validity of naked functions. -use rustc_ast::{Attribute, InlineAsmOptions}; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_ast::InlineAsmOptions; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; @@ -13,73 +13,69 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; -fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx }); -} +use crate::errors::{ + CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, + NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, + UndefinedNakedFunctionAbi, +}; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_naked_functions, ..*providers }; } -struct CheckNakedFunctions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { - fn visit_fn( - &mut self, - fk: FnKind<'_>, - _fd: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - span: Span, - hir_id: HirId, - ) { - let ident_span; - let fn_header; - - match fk { - FnKind::Closure => { - // Closures with a naked attribute are rejected during attribute - // check. Don't validate them any further. - return; - } - FnKind::ItemFn(ident, _, ref header, ..) => { - ident_span = ident.span; - fn_header = header; - } - - FnKind::Method(ident, ref sig, ..) => { - ident_span = ident.span; - fn_header = &sig.header; - } +fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + let items = tcx.hir_module_items(module_def_id); + for def_id in items.definitions() { + if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + continue; } - let attrs = self.tcx.hir().attrs(hir_id); - let naked = attrs.iter().any(|attr| attr.has_name(sym::naked)); - if naked { - let body = self.tcx.hir().body(body_id); - check_abi(self.tcx, hir_id, fn_header.abi, ident_span); - check_no_patterns(self.tcx, body.params); - check_no_parameters_use(self.tcx, body); - check_asm(self.tcx, body, span); - check_inline(self.tcx, attrs); + let naked = tcx.has_attr(def_id.to_def_id(), sym::naked); + if !naked { + continue; } + + let (fn_header, body_id) = match tcx.hir().get_by_def_id(def_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body_id), + .. + }) => (sig.header, *body_id), + _ => continue, + }; + + let body = tcx.hir().body(body_id); + check_abi(tcx, def_id, fn_header.abi); + check_no_patterns(tcx, body.params); + check_no_parameters_use(tcx, body); + check_asm(tcx, def_id, body); + check_inline(tcx, def_id); } } /// Check that the function isn't inlined. -fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) { - for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) { - tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit(); +fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline); + for attr in attrs { + tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span }); } } /// Checks that function uses non-Rust ABI. -fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) { +fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { if abi == Abi::Rust { - tcx.struct_span_lint_hir(UNDEFINED_NAKED_FUNCTION_ABI, hir_id, fn_ident_span, |lint| { - lint.build("Rust ABI is unsupported in naked functions").emit(); - }); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let span = tcx.def_span(def_id); + tcx.emit_spanned_lint( + UNDEFINED_NAKED_FUNCTION_ABI, + hir_id, + span, + UndefinedNakedFunctionAbi, + ); } } @@ -88,14 +84,9 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { for param in params { match param.pat.kind { hir::PatKind::Wild - | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {} + | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {} _ => { - tcx.sess - .struct_span_err( - param.pat.span, - "patterns not allowed in naked function parameters", - ) - .emit(); + tcx.sess.emit_err(NoPatterns { span: param.pat.span }); } } } @@ -125,14 +116,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { )) = expr.kind { if self.params.contains(var_hir_id) { - self.tcx - .sess - .struct_span_err( - expr.span, - "referencing function parameters is not allowed in naked functions", - ) - .help("follow the calling convention in asm block to use parameters") - .emit(); + self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span }); return; } } @@ -141,32 +125,27 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { } /// Checks that function body contains a single inline assembly block. -fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span) { +fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { // Ok. } else { - let mut diag = struct_span_err!( - tcx.sess, - fn_span, - E0787, - "naked functions must contain a single asm block" - ); - let mut must_show_error = false; let mut has_asm = false; let mut has_err = false; + let mut multiple_asms = vec![]; + let mut non_asms = vec![]; for &(kind, span) in &this.items { match kind { ItemKind::Asm if has_asm => { must_show_error = true; - diag.span_label(span, "multiple asm blocks are unsupported in naked functions"); + multiple_asms.push(span); } ItemKind::Asm => has_asm = true, ItemKind::NonAsm => { must_show_error = true; - diag.span_label(span, "non-asm is unsupported in naked functions"); + non_asms.push(span); } ItemKind::Err => has_err = true, } @@ -176,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span // errors, then don't show an additional error. This allows for appending/prepending // `compile_error!("...")` statements and reduces error noise. if must_show_error || !has_err { - diag.emit(); - } else { - diag.cancel(); + tcx.sess.emit_err(NakedFunctionsAsmBlock { + span: tcx.def_span(def_id), + multiple_asms, + non_asms, + }); } } } @@ -259,13 +240,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { }) .collect(); if !unsupported_operands.is_empty() { - struct_span_err!( - self.tcx.sess, - unsupported_operands, - E0787, - "only `const` and `sym` operands are supported in naked functions", - ) - .emit(); + self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands }); } let unsupported_options: Vec<&'static str> = [ @@ -281,14 +256,10 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .collect(); if !unsupported_options.is_empty() { - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(NakedFunctionsAsmOptions { span, - E0787, - "asm options unsupported in naked functions: {}", - unsupported_options.join(", ") - ) - .emit(); + unsupported_options: unsupported_options.join(", "), + }); } if !asm.options.contains(InlineAsmOptions::NORETURN) { @@ -298,20 +269,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1) .shrink_to_hi(); - struct_span_err!( - self.tcx.sess, - span, - E0787, - "asm in naked functions must use `noreturn` option" - ) - .span_suggestion( - last_span, - "consider specifying that the asm block is responsible \ - for returning from the function", - ", options(noreturn)", - Applicability::MachineApplicable, - ) - .emit(); + self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span }); } } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index f7e3fac6b..50070869a 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -12,7 +12,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc_middle::middle::privacy; +use rustc_middle::middle::privacy::{self, Level}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::config::CrateType; @@ -29,7 +29,7 @@ fn item_might_be_inlined(tcx: TyCtxt<'_>, item: &hir::Item<'_>, attrs: &CodegenF match item.kind { hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => true, hir::ItemKind::Impl { .. } | hir::ItemKind::Fn(..) => { - let generics = tcx.generics_of(item.def_id); + let generics = tcx.generics_of(item.owner_id); generics.requires_monomorphization(tcx) } _ => false, @@ -42,7 +42,7 @@ fn method_might_be_inlined( impl_src: LocalDefId, ) -> bool { let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id().owner.to_def_id()); - let generics = tcx.generics_of(impl_item.def_id); + let generics = tcx.generics_of(impl_item.owner_id); if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) { return true; } @@ -116,6 +116,17 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { intravisit::walk_expr(self, expr) } + + fn visit_inline_asm(&mut self, asm: &'tcx hir::InlineAsm<'tcx>, id: hir::HirId) { + for (op, _) in asm.operands { + if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op { + if let Some(def_id) = def_id.as_local() { + self.reachable_symbols.insert(def_id); + } + } + } + intravisit::walk_inline_asm(self, asm, id); + } } impl<'tcx> ReachableContext<'tcx> { @@ -153,9 +164,9 @@ impl<'tcx> ReachableContext<'tcx> { hir::ImplItemKind::Fn(..) => { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let impl_did = self.tcx.hir().get_parent_item(hir_id); - method_might_be_inlined(self.tcx, impl_item, impl_did) + method_might_be_inlined(self.tcx, impl_item, impl_did.def_id) } - hir::ImplItemKind::TyAlias(_) => false, + hir::ImplItemKind::Type(_) => false, }, Some(_) => false, None => false, // This will happen for default methods. @@ -216,7 +227,7 @@ impl<'tcx> ReachableContext<'tcx> { if item_might_be_inlined( self.tcx, &item, - self.tcx.codegen_fn_attrs(item.def_id), + self.tcx.codegen_fn_attrs(item.owner_id), ) { self.visit_nested_body(body); } @@ -271,7 +282,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit_nested_body(body) } } - hir::ImplItemKind::TyAlias(_) => {} + hir::ImplItemKind::Type(_) => {} }, Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), @@ -303,13 +314,13 @@ fn check_item<'tcx>( tcx: TyCtxt<'tcx>, id: hir::ItemId, worklist: &mut Vec<LocalDefId>, - access_levels: &privacy::AccessLevels, + effective_visibilities: &privacy::EffectiveVisibilities, ) { - if has_custom_linkage(tcx, id.def_id) { - worklist.push(id.def_id); + if has_custom_linkage(tcx, id.owner_id.def_id) { + worklist.push(id.owner_id.def_id); } - if !matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + if !matches!(tcx.def_kind(id.owner_id), DefKind::Impl) { return; } @@ -318,8 +329,8 @@ fn check_item<'tcx>( if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = item.kind { - if !access_levels.is_reachable(item.def_id) { - worklist.extend(items.iter().map(|ii_ref| ii_ref.id.def_id)); + if !effective_visibilities.is_reachable(item.owner_id.def_id) { + worklist.extend(items.iter().map(|ii_ref| ii_ref.id.owner_id.def_id)); let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else { unreachable!(); @@ -354,7 +365,7 @@ fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { } fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { - let access_levels = &tcx.privacy_access_levels(()); + let effective_visibilities = &tcx.effective_visibilities(()); let any_library = tcx.sess.crate_types().iter().any(|ty| { @@ -373,7 +384,13 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { // If other crates link to us, they're going to expect to be able to // use the lang items, so we need to be sure to mark them as // exported. - reachable_context.worklist.extend(access_levels.map.keys()); + reachable_context.worklist = effective_visibilities + .iter() + .filter_map(|(&id, effective_vis)| { + effective_vis.is_public_at_level(Level::ReachableThroughImplTrait).then_some(id) + }) + .collect::<Vec<_>>(); + for item in tcx.lang_items().items().iter() { if let Some(def_id) = *item { if let Some(def_id) = def_id.as_local() { @@ -393,12 +410,12 @@ fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { let crate_items = tcx.hir_crate_items(()); for id in crate_items.items() { - check_item(tcx, id, &mut reachable_context.worklist, access_levels); + check_item(tcx, id, &mut reachable_context.worklist, effective_visibilities); } for id in crate_items.impl_items() { - if has_custom_linkage(tcx, id.def_id) { - reachable_context.worklist.push(id.def_id); + if has_custom_linkage(tcx, id.owner_id.def_id) { + reachable_context.worklist.push(id.owner_id.def_id); } } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index ca6a2ac3d..78afa2f25 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,23 +1,30 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use attr::StabilityLevel; -use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason}; +use crate::errors::{ + self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr, + FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability, + MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable, + UnknownFeature, UselessStability, +}; +use rustc_attr::{ + self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable, + UnstableReason, VERSION_PLACEHOLDER, +}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index}; use rustc_middle::ty::{query::Providers, TyCtxt}; use rustc_session::lint; use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; -use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -120,16 +127,12 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| { - lint.build("this `#[deprecated]` annotation has no effect") - .span_suggestion_short( - *span, - "remove the unnecessary deprecation attribute", - "", - rustc_errors::Applicability::MachineApplicable, - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + USELESS_DEPRECATED, + hir_id, + *span, + errors::DeprecatedAnnotationHasNoEffect { span: *span }, + ); } // `Deprecation` is just two pointers, no need to intern it @@ -161,7 +164,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { return; } - let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); + let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); let mut const_span = None; let const_stab = const_stab.map(|(const_stab, const_span_node)| { @@ -180,7 +183,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if !self.in_trait_impl || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id())) { - missing_const_err(&self.tcx.sess, fn_sig.span, const_span); + self.tcx + .sess + .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span }); } } } @@ -198,26 +203,23 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { if stab.is_none() { - struct_span_err!( - self.tcx.sess, - *span, - E0549, - "deprecated attribute must be paired with \ - either stable or unstable attribute" - ) - .emit(); + self.tcx.sess.emit_err(DeprecatedAttribute { span: *span }); } } + if let Some((body_stab, _span)) = body_stab { + // FIXME: check that this item can have body stability + + self.index.default_body_stab_map.insert(def_id, body_stab); + debug!(?self.index.default_body_stab_map); + } + let stab = stab.map(|(stab, span)| { // Error if prohibited, or can't inherit anything from a container. if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) { - self.tcx.sess.struct_span_err(span,"this stability annotation is useless") - .span_label(span, "useless stability annotation") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx.sess.emit_err(UselessStability { span, item_sp }); } debug!("annotate: found {:?}", stab); @@ -233,19 +235,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { match stab_v.parse::<u64>() { Err(_) => { - self.tcx.sess.struct_span_err(span, "invalid stability version found") - .span_label(span, "invalid stability version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx.sess.emit_err(InvalidStability { span, item_sp }); break; } Ok(stab_vp) => match dep_v.parse::<u64>() { Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { Ordering::Less => { - self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated") - .span_label(span, "invalid version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx + .sess + .emit_err(CannotStabilizeDeprecated { span, item_sp }); break; } Ordering::Equal => continue, @@ -253,10 +251,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }, Err(_) => { if dep_v != "TBD" { - self.tcx.sess.struct_span_err(span, "invalid deprecation version found") - .span_label(span, "invalid deprecation version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx + .sess + .emit_err(InvalidDeprecationVersion { span, item_sp }); } break; } @@ -265,7 +262,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { + if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = + stab + { self.index.implications.insert(implied_by, feature); } @@ -379,7 +378,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } self.annotate( - i.def_id, + i.owner_id.def_id, i.span, fn_sig, kind, @@ -398,7 +397,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { }; self.annotate( - ti.def_id, + ti.owner_id.def_id, ti.span, fn_sig, AnnotationKind::Required, @@ -421,7 +420,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { }; self.annotate( - ii.def_id, + ii.owner_id.def_id, ii.span, fn_sig, kind, @@ -434,7 +433,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ); } - fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { + fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { self.annotate( self.tcx.hir().local_def_id(var.id), var.span, @@ -452,12 +451,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, - InheritStability::No, + InheritStability::Yes, |_| {}, ); } - intravisit::walk_variant(v, var, g, item_id) + intravisit::walk_variant(v, var) }, ) } @@ -479,7 +478,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { self.annotate( - i.def_id, + i.owner_id.def_id, i.span, None, AnnotationKind::Required, @@ -517,15 +516,18 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { struct MissingStabilityAnnotations<'tcx> { tcx: TyCtxt<'tcx>, - access_levels: &'tcx AccessLevels, + effective_visibilities: &'tcx EffectiveVisibilities, } impl<'tcx> MissingStabilityAnnotations<'tcx> { fn check_missing_stability(&self, def_id: LocalDefId, span: Span) { let stab = self.tcx.stability().local_stability(def_id); - if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) { + if !self.tcx.sess.opts.test + && stab.is_none() + && self.effective_visibilities.is_reachable(def_id) + { let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr)); + self.tcx.sess.emit_err(MissingStabilityAttr { span, descr }); } } @@ -541,11 +543,11 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { .lookup_stability(def_id) .map_or(false, |stability| stability.level.is_stable()); let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none(); - let is_reachable = self.access_levels.is_reachable(def_id); + let is_reachable = self.effective_visibilities.is_reachable(def_id); if is_const && is_stable && missing_const_stability_attribute && is_reachable { let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute")); + self.tcx.sess.emit_err(MissingConstStabAttr { span, descr }); } } } @@ -567,32 +569,35 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) | hir::ItemKind::ForeignMod { .. } ) { - self.check_missing_stability(i.def_id, i.span); + self.check_missing_stability(i.owner_id.def_id, i.span); } // Ensure stable `const fn` have a const stability attribute. - self.check_missing_const_stability(i.def_id, i.span); + self.check_missing_const_stability(i.owner_id.def_id, i.span); intravisit::walk_item(self, i) } fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - self.check_missing_stability(ti.def_id, ti.span); + self.check_missing_stability(ti.owner_id.def_id, ti.span); intravisit::walk_trait_item(self, ti); } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id()); if self.tcx.impl_trait_ref(impl_def_id).is_none() { - self.check_missing_stability(ii.def_id, ii.span); - self.check_missing_const_stability(ii.def_id, ii.span); + self.check_missing_stability(ii.owner_id.def_id, ii.span); + self.check_missing_const_stability(ii.owner_id.def_id, ii.span); } intravisit::walk_impl_item(self, ii); } - fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { + fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { self.check_missing_stability(self.tcx.hir().local_def_id(var.id), var.span); - intravisit::walk_variant(self, var, g, item_id); + if let Some(ctor_hir_id) = var.data.ctor_hir_id() { + self.check_missing_stability(self.tcx.hir().local_def_id(ctor_hir_id), var.span); + } + intravisit::walk_variant(self, var); } fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { @@ -601,7 +606,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - self.check_missing_stability(i.def_id, i.span); + self.check_missing_stability(i.owner_id.def_id, i.span); intravisit::walk_foreign_item(self, i); } // Note that we don't need to `check_missing_stability` for default generic parameters, @@ -613,6 +618,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { let mut index = Index { stab_map: Default::default(), const_stab_map: Default::default(), + default_body_stab_map: Default::default(), depr_map: Default::default(), implications: Default::default(), }; @@ -673,6 +679,9 @@ pub(crate) fn provide(providers: &mut Providers) { stability_implications: |tcx, _| tcx.stability().implications.clone(), lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()), lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()), + lookup_default_body_stability: |tcx, id| { + tcx.stability().local_default_body_stability(id.expect_local()) + }, lookup_deprecation_entry: |tcx, id| { tcx.stability().local_deprecation_entry(id.expect_local()) }, @@ -703,7 +712,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { return; } - let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.def_id) else { + let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else { return; }; let def_id = cnum.as_def_id(); @@ -723,7 +732,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let features = self.tcx.features(); if features.staged_api { let attrs = self.tcx.hir().attrs(item.hir_id()); - let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span); + let (stab, const_stab, _) = + attr::find_stability(&self.tcx.sess, attrs, item.span); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because @@ -738,10 +748,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id(), span, - |lint| {lint - .build("an `#[unstable]` annotation here has no effect") - .note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information") - .emit();} + "an `#[unstable]` annotation here has no effect", + |lint| lint.note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information") ); } } @@ -752,16 +760,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { && *constness == hir::Constness::Const && const_stab.map_or(false, |(stab, _)| stab.is_const_stable()) { - self.tcx - .sess - .struct_span_err(item.span, "trait implementations cannot be const stable yet") - .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information") - .emit(); + self.tcx.sess.emit_err(TraitImplConstStable { span: item.span }); } } for impl_item_ref in *items { - let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id); + let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id); if let Some(def_id) = impl_item.trait_item_def_id { // Pass `None` to skip deprecation warnings. @@ -816,7 +820,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // added, such as `core::intrinsics::transmute` let parents = path.segments.iter().rev().skip(1); for path_segment in parents { - if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) { + if let Some(def_id) = path_segment.res.opt_def_id() { // use `None` for id to prevent deprecation check self.tcx.check_stability_allow_unstable( def_id, @@ -855,7 +859,7 @@ fn is_unstable_reexport<'tcx>(tcx: TyCtxt<'tcx>, id: hir::HirId) -> bool { } // If this is a path that isn't a use, we don't need to do anything special - if !matches!(tcx.hir().item(hir::ItemId { def_id }).kind, ItemKind::Use(..)) { + if !matches!(tcx.hir().expect_item(def_id).kind, ItemKind::Use(..)) { return false; } @@ -890,8 +894,25 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { if let TyKind::Never = t.kind { self.fully_stable = false; } + if let TyKind::BareFn(f) = t.kind { + if rustc_target::spec::abi::is_stable(f.abi.name()).is_err() { + self.fully_stable = false; + } + } intravisit::walk_ty(self, t) } + + fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { + for ty in fd.inputs { + self.visit_ty(ty) + } + if let hir::FnRetTy::Return(output_ty) = fd.output { + match output_ty.kind { + TyKind::Never => {} // `-> !` is stable + _ => self.visit_ty(output_ty), + } + } + } } /// Given the list of enabled features that were not language features (i.e., that @@ -901,8 +922,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let is_staged_api = tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api; if is_staged_api { - let access_levels = &tcx.privacy_access_levels(()); - let mut missing = MissingStabilityAnnotations { tcx, access_levels }; + let effective_visibilities = &tcx.effective_visibilities(()); + let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities }; missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID)); tcx.hir().walk_toplevel_module(&mut missing); tcx.hir().visit_all_item_likes_in_crate(&mut missing); @@ -917,7 +938,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } if !lang_features.insert(feature) { // Warn if the user enables a lang feature multiple times. - duplicate_feature_err(tcx.sess, span, feature); + tcx.sess.emit_err(DuplicateFeatureErr { span, feature }); } } @@ -925,18 +946,14 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let mut remaining_lib_features = FxIndexMap::default(); for (feature, span) in declared_lib_features { if !tcx.sess.opts.unstable_features.is_nightly_build() { - struct_span_err!( - tcx.sess, - *span, - E0554, - "`#![feature]` may not be used on the {} release channel", - env!("CFG_RELEASE_CHANNEL") - ) - .emit(); + tcx.sess.emit_err(FeatureOnlyOnNightly { + span: *span, + release_channel: env!("CFG_RELEASE_CHANNEL"), + }); } if remaining_lib_features.contains_key(&feature) { // Warn if the user enables a lib feature multiple times. - duplicate_feature_err(tcx.sess, *span, *feature); + tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature }); } remaining_lib_features.insert(feature, *span); } @@ -949,56 +966,106 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { remaining_lib_features.remove(&sym::libc); remaining_lib_features.remove(&sym::test); - // We always collect the lib features declared in the current crate, even if there are - // no unknown features, because the collection also does feature attribute validation. - let local_defined_features = tcx.lib_features(()); - let mut all_lib_features: FxHashMap<_, _> = - local_defined_features.to_vec().iter().map(|el| *el).collect(); - let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); - for &cnum in tcx.crates(()) { - implications.extend(tcx.stability_implications(cnum)); - all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el)); - } - - // Check that every feature referenced by an `implied_by` exists (for features defined in the - // local crate). - for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) { - // Only `implied_by` needs to be checked, `feature` is guaranteed to exist. - if !all_lib_features.contains_key(implied_by) { - let span = local_defined_features - .stable - .get(feature) - .map(|(_, span)| span) - .or_else(|| local_defined_features.unstable.get(feature)) - .expect("feature that implied another does not exist"); - tcx.sess - .struct_span_err( - *span, - format!("feature `{implied_by}` implying `{feature}` does not exist"), - ) - .emit(); - } - } - - if !remaining_lib_features.is_empty() { - for (feature, since) in all_lib_features.iter() { + /// For each feature in `defined_features`.. + /// + /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in + /// the current crate), check if it is stable (or partially stable) and thus an unnecessary + /// attribute. + /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by` + /// from the current crate), then remove it from the remaining implications. + /// + /// Once this function has been invoked for every feature (local crate and all extern crates), + /// then.. + /// + /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that + /// does not exist. + /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that + /// does not exist. + /// + /// By structuring the code in this way: checking the features defined from each crate one at a + /// time, less loading from metadata is performed and thus compiler performance is improved. + fn check_features<'tcx>( + tcx: TyCtxt<'tcx>, + remaining_lib_features: &mut FxIndexMap<&Symbol, Span>, + remaining_implications: &mut FxHashMap<Symbol, Symbol>, + defined_features: &[(Symbol, Option<Symbol>)], + all_implications: &FxHashMap<Symbol, Symbol>, + ) { + for (feature, since) in defined_features { if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) { // Warn if the user has enabled an already-stable lib feature. - if let Some(implies) = implications.get(&feature) { + if let Some(implies) = all_implications.get(&feature) { unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since); } else { unnecessary_stable_feature_lint(tcx, *span, *feature, *since); } + } - remaining_lib_features.remove(&feature); - if remaining_lib_features.is_empty() { + remaining_lib_features.remove(feature); + + // `feature` is the feature doing the implying, but `implied_by` is the feature with + // the attribute that establishes this relationship. `implied_by` is guaranteed to be a + // feature defined in the local crate because `remaining_implications` is only the + // implications from this crate. + remaining_implications.remove(feature); + + if remaining_lib_features.is_empty() && remaining_implications.is_empty() { break; } } } + // All local crate implications need to have the feature that implies it confirmed to exist. + let mut remaining_implications = + tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); + + // We always collect the lib features declared in the current crate, even if there are + // no unknown features, because the collection also does feature attribute validation. + let local_defined_features = tcx.lib_features(()).to_vec(); + if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() { + // Loading the implications of all crates is unavoidable to be able to emit the partial + // stabilization diagnostic, but it can be avoided when there are no + // `remaining_lib_features`. + let mut all_implications = remaining_implications.clone(); + for &cnum in tcx.crates(()) { + all_implications.extend(tcx.stability_implications(cnum)); + } + + check_features( + tcx, + &mut remaining_lib_features, + &mut remaining_implications, + local_defined_features.as_slice(), + &all_implications, + ); + + for &cnum in tcx.crates(()) { + if remaining_lib_features.is_empty() && remaining_implications.is_empty() { + break; + } + check_features( + tcx, + &mut remaining_lib_features, + &mut remaining_implications, + tcx.defined_lib_features(cnum).to_vec().as_slice(), + &all_implications, + ); + } + } + for (feature, span) in remaining_lib_features { - struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit(); + tcx.sess.emit_err(UnknownFeature { span, feature: *feature }); + } + + for (implied_by, feature) in remaining_implications { + let local_defined_features = tcx.lib_features(()); + let span = *local_defined_features + .stable + .get(&feature) + .map(|(_, span)| span) + .or_else(|| local_defined_features.unstable.get(&feature)) + .expect("feature that implied another does not exist"); + tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by }); } // FIXME(#44232): the `used_features` table no longer exists, so we @@ -1012,52 +1079,43 @@ fn unnecessary_partially_stable_feature_lint( implies: Symbol, since: Symbol, ) { - tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { - lint.build(&format!( + tcx.struct_span_lint_hir( + lint::builtin::STABLE_FEATURES, + hir::CRATE_HIR_ID, + span, + format!( "the feature `{feature}` has been partially stabilized since {since} and is succeeded \ by the feature `{implies}`" - )) - .span_suggestion( - span, - &format!( + ), + |lint| { + lint.span_suggestion( + span, + &format!( "if you are using features which are still unstable, change to using `{implies}`" ), - implies, - Applicability::MaybeIncorrect, - ) - .span_suggestion( - tcx.sess.source_map().span_extend_to_line(span), - "if you are using features which are now stable, remove this line", - "", - Applicability::MaybeIncorrect, - ) - .emit(); - }); + implies, + Applicability::MaybeIncorrect, + ) + .span_suggestion( + tcx.sess.source_map().span_extend_to_line(span), + "if you are using features which are now stable, remove this line", + "", + Applicability::MaybeIncorrect, + ) + }, + ); } -fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { - tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { - lint.build(&format!( - "the feature `{feature}` has been stable since {since} and no longer requires an \ - attribute to enable", - )) - .emit(); +fn unnecessary_stable_feature_lint( + tcx: TyCtxt<'_>, + span: Span, + feature: Symbol, + mut since: Symbol, +) { + if since.as_str() == VERSION_PLACEHOLDER { + since = rust_version_symbol(); + } + tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, format!("the feature `{feature}` has been stable since {since} and no longer requires an attribute to enable"), |lint| { + lint }); } - -fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { - struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature) - .emit(); -} - -fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) { - const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \ - and `#[rustc_const_stable]` require \ - the function or method to be `const`"; - - session - .struct_span_err(fn_sig_span, ERROR_MSG) - .span_help(fn_sig_span, "make the function or method const") - .span_label(const_span, "attribute specified here") - .emit(); -} diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index c48b4ecf8..959ee600c 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -1,13 +1,17 @@ //! Validity checking for weak lang items use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; use rustc_hir::lang_items::{self, LangItem}; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::required; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use crate::errors::{ + AllocFuncRequired, MissingAllocErrorHandler, MissingLangItem, MissingPanicHandler, + UnknownExternLangItem, +}; + /// Checks the crate for usage of weak lang items, returning a vector of all the /// language items required by this crate, but not defined yet. pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItems) { @@ -30,15 +34,8 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem items.missing.push(item); } } else { - let span = tcx.def_span(id.def_id); - struct_span_err!( - tcx.sess, - span, - E0264, - "unknown external lang item: `{}`", - lang_item - ) - .emit(); + let span = tcx.def_span(id.owner_id); + tcx.sess.emit_err(UnknownExternLangItem { span, lang_item }); } } } @@ -71,20 +68,14 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { for (name, &item) in WEAK_ITEMS_REFS.iter() { if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() { if item == LangItem::PanicImpl { - tcx.sess.err("`#[panic_handler]` function required, but not found"); + tcx.sess.emit_err(MissingPanicHandler); } else if item == LangItem::Oom { if !tcx.features().default_alloc_error_handler { - tcx.sess.err("`#[alloc_error_handler]` function required, but not found"); - tcx.sess.note_without_error("use `#![feature(default_alloc_error_handler)]` for a default error handler"); + tcx.sess.emit_err(AllocFuncRequired); + tcx.sess.emit_note(MissingAllocErrorHandler); } } else { - tcx - .sess - .diagnostic() - .struct_err(&format!("language item required, but not found: `{}`", name)) - .note(&format!("this can occur when a binary crate with `#![no_std]` is compiled for a target where `{}` is defined in the standard library", name)) - .help(&format!("you may be able to compile for a target that doesn't need `{}`, specify a target with `--target` or in `.cargo/config`", name)) - .emit(); + tcx.sess.emit_err(MissingLangItem { name: *name }); } } } |