diff options
Diffstat (limited to 'compiler/rustc_passes/src')
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 573 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/check_export.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/dead.rs | 108 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/diagnostic_items.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/errors.rs | 228 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/input_stats.rs | 77 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lib.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lib_features.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/liveness.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/loops.rs | 402 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/stability.rs | 11 | 
11 files changed, 488 insertions, 935 deletions
| diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dc29b03083f..877bb9be289 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; -use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; @@ -30,11 +30,13 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; +use rustc_session::lint; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; +use rustc_span::edition::Edition; use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; @@ -116,7 +118,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { + let mut style = None; match attr { + Attribute::Parsed(AttributeKind::SkipDuringMethodDispatch { + span: attr_span, + .. + }) => { + self.check_must_be_applied_to_trait(*attr_span, span, target); + } Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => { self.check_confusables(*first_span, target); } @@ -124,6 +133,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Stability { span, .. } | AttributeKind::ConstStability { span, .. }, ) => self.check_stability_promotable(*span, target), + Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below + Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { + self.check_inline(hir_id, *attr_span, span, kind, target) + } + Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => { + self.check_optimize(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => { + self.check_loop_match(hir_id, *attr_span, target) + } + Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => { + self.check_const_continue(hir_id, *attr_span, target) + } Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self .check_allow_internal_unstable( hir_id, @@ -132,7 +154,57 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target, attrs, ), - _ => { + Attribute::Parsed(AttributeKind::AllowConstFnUnstable { .. }) => { + self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) + } + Attribute::Parsed(AttributeKind::Deprecation { .. }) => { + self.check_deprecated(hir_id, attr, span, target) + } + Attribute::Parsed(AttributeKind::DocComment { .. }) => { /* `#[doc]` is actually a lot more than just doc comments, so is checked below*/ + } + Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ + } + + &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => { + self.check_rustc_pub_transparent(attr_span, span, attrs) + } + Attribute::Parsed(AttributeKind::Cold(attr_span)) => { + self.check_cold(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::ExportName { span: attr_span, .. }) => { + self.check_export_name(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => { + self.check_align(span, target, *align, *repr_span) + } + Attribute::Parsed(AttributeKind::Naked(attr_span)) => { + self.check_naked(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => { + self.check_track_caller(hir_id, *attr_span, attrs, span, target) + } + Attribute::Parsed( + AttributeKind::BodyStability { .. } + | AttributeKind::ConstStabilityIndirect + | AttributeKind::MacroTransparency(_), + ) => { /* do nothing */ } + Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { + self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { + self.check_may_dangle(hir_id, *attr_span) + } + Attribute::Parsed(AttributeKind::MustUse { span, .. }) => { + self.check_must_use(hir_id, *span, target) + } + Attribute::Parsed(AttributeKind::NoMangle(attr_span)) => { + self.check_no_mangle(hir_id, *attr_span, span, target) + } + Attribute::Parsed(AttributeKind::Used { span: attr_span, .. }) => { + self.check_used(*attr_span, target, span); + } + Attribute::Unparsed(attr_item) => { + style = Some(attr_item.style); match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend, ..] => { self.check_do_not_recommend(attr.span(), hir_id, target, attr, item) @@ -140,9 +212,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } @@ -152,50 +222,40 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_target_feature(hir_id, attr, span, target, attrs) } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::track_caller, ..] => { - self.check_track_caller(hir_id, attr.span(), attrs, span, target) - } [sym::doc, ..] => self.check_doc_attrs( attr, + attr_item.style, hir_id, target, &mut specified_inline, &mut doc_aliases, ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), [sym::rustc_layout_scalar_valid_range_start, ..] | [sym::rustc_layout_scalar_valid_range_end, ..] => { self.check_rustc_layout_scalar_valid_range(attr, span, target) } [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), - [sym::rustc_allow_const_fn_unstable, ..] => { - self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) - } [sym::rustc_std_internal_symbol, ..] => { self.check_rustc_std_internal_symbol(attr, span, target) } - [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), - [sym::rustc_as_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } [sym::rustc_no_implicit_autorefs, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_legacy_const_generics, ..] => { self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) } [sym::rustc_lint_query_instability, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_untracked_query_information, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_diagnostics, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) + self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), [sym::rustc_lint_opt_deny_field_access, ..] => { @@ -209,11 +269,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | [sym::rustc_must_implement_one_of, ..] | [sym::rustc_deny_explicit_impl, ..] | [sym::rustc_do_not_implement_via_object, ..] - | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), + | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target), [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(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, target), - [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr), [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), [sym::rustc_allow_incoherent_impl, ..] => { self.check_allow_incoherent_impl(attr, span, target) @@ -224,12 +282,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), - [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), - [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) } @@ -265,7 +320,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_type_const(hir_id,attr, target); } [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs), [ // ok sym::allow @@ -282,8 +336,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::cfi_encoding // FIXME(cfi_encoding) | sym::pointee // FIXME(derive_coerce_pointee) | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) - | sym::used // handled elsewhere to restrict to static items - | sym::repr // handled elsewhere to restrict to type decls items | sym::instruction_set // broken on stable!!! | sym::windows_subsystem // broken on stable!!! | sym::patchable_function_entry // FIXME(patchable_function_entry) @@ -328,14 +380,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - match attr.style() { - ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + match style { + Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::OuterCrateLevelAttr, ), - ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), @@ -349,19 +401,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); } - self.check_unused_attribute(hir_id, attr) + self.check_unused_attribute(hir_id, attr, style) } self.check_repr(attrs, span, target, item, hir_id); - self.check_used(attrs, target, span); self.check_rustc_force_inline(hir_id, attrs, span, target); + self.check_mix_no_mangle_export(hir_id, attrs); } - fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { + fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredAttrWithMacro { sym }, ); } @@ -421,7 +473,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if an `#[inline]` is applied to a function or a closure. - fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_inline( + &self, + hir_id: HirId, + attr_span: Span, + defn_span: Span, + kind: &InlineAttr, + target: Target, + ) { match target { Target::Fn | Target::Closure @@ -430,7 +489,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredInlineAttrFnProto, ) } @@ -441,25 +500,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::AssocConst => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::IgnoredInlineAttrConstants, ), // FIXME(#80564): Same for fields, arms, and macro defs Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline") + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "inline") } _ => { - self.dcx().emit_err(errors::InlineNotFnOrClosure { - attr_span: attr.span(), - defn_span: span, - }); + self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span }); } } // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. if let Some(did) = hir_id.as_owner() && self.tcx.def_kind(did).has_codegen_attrs() - && !matches!(attr.meta_item_list().as_deref(), Some([item]) if item.has_name(sym::never)) + && kind != &InlineAttr::Never { let attrs = self.tcx.codegen_fn_attrs(did); // Not checking naked as `#[inline]` is forbidden for naked functions anyways. @@ -467,7 +523,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::InlineIgnoredForExported {}, ); } @@ -508,7 +564,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[optimize(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { let is_valid = matches!( target, Target::Fn @@ -517,7 +573,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); if !is_valid { self.dcx().emit_err(errors::OptimizeInvalidTarget { - attr_span: attr.span(), + attr_span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -584,55 +640,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[naked]` is applied to a function definition. - fn check_naked( - &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, - attrs: &[Attribute], - ) { - // many attributes don't make sense in combination with #[naked]. - // Notable attributes that are incompatible with `#[naked]` are: - // - // * `#[inline]` - // * `#[track_caller]` - // * `#[test]`, `#[ignore]`, `#[should_panic]` - // - // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains - // accurate. - const ALLOW_LIST: &[rustc_span::Symbol] = &[ - // conditional compilation - sym::cfg_trace, - sym::cfg_attr_trace, - // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`) - sym::test, - sym::ignore, - sym::should_panic, - sym::bench, - // diagnostics - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - // FIXME(jdonszelmann): not used, because already a new-style attr (ugh) - sym::deprecated, - sym::must_use, - // abi, linking and FFI - sym::export_name, - sym::link_section, - sym::linkage, - sym::no_mangle, - sym::naked, - sym::instruction_set, - sym::repr, - sym::rustc_std_internal_symbol, - // code generation - sym::cold, - // documentation - sym::doc, - ]; - + fn check_naked(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => { @@ -650,60 +658,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) .emit(); } - - for other_attr in attrs { - // this covers "sugared doc comments" of the form `/// ...` - // it does not cover `#[doc = "..."]`, which is handled below - if other_attr.is_doc_comment() { - continue; - } - - // FIXME(jdonszelmann): once naked uses new-style parsing, - // this check can be part of the parser and be removed here - match other_attr { - Attribute::Parsed( - AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. }, - ) => { - continue; - } - _ => {} - } - - if other_attr.has_name(sym::target_feature) { - if !self.tcx.features().naked_functions_target_feature() { - feature_err( - &self.tcx.sess, - sym::naked_functions_target_feature, - other_attr.span(), - "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions", - ).emit(); - - return; - } else { - continue; - } - } - - if !other_attr.has_any_name(ALLOW_LIST) - && !matches!(other_attr.path().as_slice(), [sym::rustfmt, ..]) - { - let path = other_attr.path(); - let path: Vec<_> = path.iter().map(|s| s.as_str()).collect(); - let other_attr_name = path.join("::"); - - self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { - span: other_attr.span(), - naked_span: attr.span(), - attr: other_attr_name, - }); - - return; - } - } } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span(), + attr_span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -776,9 +734,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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 { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); - } + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "track_caller"); } _ => { self.dcx().emit_err(errors::TrackedCallerWrongLocation { @@ -820,7 +776,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "non_exhaustive"); } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { @@ -840,7 +796,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "marker"); } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { @@ -894,7 +850,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "target_feature"); } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1153,7 +1109,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// the first `inline`/`no_inline` attribute. fn check_doc_inline( &self, - attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, target: Target, @@ -1183,8 +1139,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInlineOnlyUse { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); } @@ -1193,7 +1148,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_doc_masked( &self, - attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, target: Target, @@ -1205,8 +1160,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedOnlyExternCrate { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); return; @@ -1219,8 +1173,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedNotExternCrateSelf { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); } @@ -1244,13 +1197,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_attr_crate_level( &self, attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { // insert a bang between `#` and `[...` let bang_span = attr.span().lo() + BytePos(1); - let sugg = (attr.style() == AttrStyle::Outer + let sugg = (style == AttrStyle::Outer && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID) .then_some(errors::AttrCrateLevelOnlySugg { attr: attr.span().with_lo(bang_span).with_hi(bang_span), @@ -1267,7 +1221,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place. - fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) { + fn check_test_attr( + &self, + attr: &Attribute, + style: AttrStyle, + meta: &MetaItemInner, + hir_id: HirId, + ) { if let Some(metas) = meta.meta_item_list() { for i_meta in metas { match (i_meta.name(), i_meta.meta_item()) { @@ -1275,7 +1235,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // Allowed everywhere like `#[doc]` } (Some(sym::no_crate_inject), _) => { - self.check_attr_crate_level(attr, meta, hir_id); + self.check_attr_crate_level(attr, style, meta, hir_id); } (_, Some(m)) => { self.tcx.emit_node_span_lint( @@ -1329,6 +1289,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_doc_attrs( &self, attr: &Attribute, + style: AttrStyle, hir_id: HirId, target: Target, specified_inline: &mut Option<(bool, Span)>, @@ -1363,7 +1324,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Some(sym::test) => { - self.check_test_attr(attr, meta, hir_id); + self.check_test_attr(attr, style, meta, hir_id); } Some( @@ -1374,25 +1335,25 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::html_root_url | sym::html_no_source, ) => { - self.check_attr_crate_level(attr, meta, hir_id); + self.check_attr_crate_level(attr, style, meta, hir_id); } Some(sym::cfg_hide) => { - if self.check_attr_crate_level(attr, meta, hir_id) { + if self.check_attr_crate_level(attr, style, meta, hir_id) { self.check_doc_cfg_hide(meta, hir_id); } } Some(sym::inline | sym::no_inline) => { - self.check_doc_inline(attr, meta, hir_id, target, specified_inline) + self.check_doc_inline(style, meta, hir_id, target, specified_inline) } - Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target), + Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target), Some(sym::cfg | sym::hidden | sym::notable_trait) => {} Some(sym::rust_logo) => { - if self.check_attr_crate_level(attr, meta, hir_id) + if self.check_attr_crate_level(attr, style, meta, hir_id) && !self.tcx.features().rustdoc_internals() { feature_err( @@ -1431,7 +1392,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownInclude { path, value: value.to_string(), - inner: match attr.style() { + inner: match style { AttrStyle::Inner => "!", AttrStyle::Outer => "", }, @@ -1524,7 +1485,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_must_use(&self, hir_id: HirId, attr_span: Span, target: Target) { if matches!( target, Target::Fn @@ -1564,7 +1525,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MustUseNoEffect { article, target }, ); } @@ -1580,7 +1541,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl. - fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) { + fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) { if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id) && matches!( param.kind, @@ -1597,11 +1558,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } - self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() }); + self.dcx().emit_err(errors::InvalidMayDangle { attr_span }); } /// Checks if `#[cold]` is applied to a non-function. - fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_cold(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an @@ -1609,7 +1570,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "cold"); } _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used @@ -1617,7 +1578,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID }, ); } @@ -1651,7 +1612,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_name"); } _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates @@ -1685,7 +1646,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_link"); } _ => { self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span }); @@ -1698,7 +1659,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[export_name]` is applied to a function or static. - fn check_export_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_export_name(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Static | Target::Fn => {} Target::Method(..) if self.is_impl_item(hir_id) => {} @@ -1707,10 +1668,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "export_name"); } _ => { - self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span }); + self.dcx().emit_err(errors::ExportName { attr_span, span }); } } } @@ -1815,15 +1776,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_applied_to_fn_or_method( &self, hir_id: HirId, - attr: &Attribute, - span: Span, + attr_span: Span, + defn_span: Span, target: Target, ) { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span(), - defn_span: span, + attr_span, + defn_span, on_crate: hir_id == CRATE_HIR_ID, }); } @@ -1860,14 +1821,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if the attribute is applied to a trait. - fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) { + fn check_must_be_applied_to_trait(&self, attr_span: Span, defn_span: Span, target: Target) { match target { Target::Trait => {} _ => { - self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { - attr_span: attr.span(), - defn_span: span, - }); + self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span, defn_span }); } } } @@ -1881,7 +1839,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "link_section"); } _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some @@ -1897,7 +1855,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[no_mangle]` is applied to a function or static. - fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_no_mangle(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { match target { Target::Static | Target::Fn => {} Target::Method(..) if self.is_impl_item(hir_id) => {} @@ -1906,7 +1864,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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"); + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "no_mangle"); } // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error // The error should specify that the item that is wrong is specifically a *foreign* fn/static @@ -1920,8 +1878,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind }, + attr_span, + errors::NoMangleForeign { span, attr_span, foreign_item_kind }, ); } _ => { @@ -1930,13 +1888,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::NoMangle { span }, ); } } } + /// Checks if the `#[align]` attributes on `item` are valid. + fn check_align(&self, span: Span, target: Target, align: Align, repr_span: Span) { + match target { + Target::Fn | Target::Method(_) => {} + Target::Struct | Target::Union | Target::Enum => { + self.dcx().emit_err(errors::AlignShouldBeReprAlign { + span: repr_span, + item: target.name(), + align_bytes: align.bytes(), + }); + } + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: repr_span, + span, + }); + } + } + + self.check_align_value(align, repr_span); + } + /// Checks if the `#[repr]` attributes on `item` are valid. fn check_repr( &self, @@ -1989,23 +1969,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Union | Target::Enum => {} Target::Fn | Target::Method(_) => { - if !self.tcx.features().fn_align() { - feature_err( - &self.tcx.sess, - sym::fn_align, - *repr_span, - fluent::passes_repr_align_function, - ) - .emit(); - } + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.name(), + }); } _ => { - self.dcx().emit_err( - errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: *repr_span, - span, - }, - ); + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); } } @@ -2063,21 +2036,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Union | Target::Enum => continue, Target::Fn | Target::Method(_) => { - feature_err( - &self.tcx.sess, - sym::fn_align, - *repr_span, - fluent::passes_repr_align_function, - ) - .emit(); + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.name(), + }); } _ => { - self.dcx().emit_err( - errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: *repr_span, - span, - }, - ); + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); } } } @@ -2140,44 +2108,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_used(&self, attrs: &[Attribute], target: Target, target_span: Span) { - let mut used_linker_span = None; - let mut used_compiler_span = None; - for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { - if target != Target::Static { - self.dcx().emit_err(errors::UsedStatic { - attr_span: attr.span(), - span: target_span, - target: target.name(), - }); - } - let inner = attr.meta_item_list(); - match inner.as_deref() { - Some([item]) if item.has_name(sym::linker) => { - if used_linker_span.is_none() { - used_linker_span = Some(attr.span()); - } - } - Some([item]) if item.has_name(sym::compiler) => { - if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span()); - } - } - Some(_) => { - // This error case is handled in rustc_hir_analysis::collect. - } - None => { - // Default case (compiler) when arg isn't defined. - if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span()); - } - } - } - } - if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) { - self.tcx - .dcx() - .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] }); + fn check_used(&self, attr_span: Span, target: Target, target_span: Span) { + if target != Target::Static { + self.dcx().emit_err(errors::UsedStatic { + attr_span, + span: target_span, + target: target.name(), + }); } } @@ -2253,9 +2190,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // `#[allow_internal_unstable]` attribute with just a lint, because we previously // 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") - } + Target::Field | Target::Arm | Target::MacroDef => self + .inline_attr_str_error_with_macro_def( + hir_id, + attr.span(), + "allow_internal_unstable", + ), _ => { self.tcx .dcx() @@ -2372,7 +2312,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { + fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) { // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very // ugly now but can 100% be removed later. if let Attribute::Parsed(p) = attr { @@ -2425,14 +2365,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) { if hir_id != CRATE_HIR_ID { - match attr.style() { - ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + match style { + Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::OuterCrateLevelAttr, ), - ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), @@ -2628,8 +2568,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, ) { - let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline)); - match (target, force_inline_attr) { + match ( + target, + find_attr!(attrs, AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span), + ) { (Target::Closure, None) => { let is_coro = matches!( self.tcx.hir_expect_expr(hir_id).kind, @@ -2641,25 +2583,54 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); let parent_did = self.tcx.hir_get_parent_item(hir_id).to_def_id(); let parent_span = self.tcx.def_span(parent_did); - let parent_force_inline_attr = - self.tcx.get_attr(parent_did, sym::rustc_force_inline); - if let Some(attr) = parent_force_inline_attr - && is_coro + + if let Some(attr_span) = find_attr!( + self.tcx.get_all_attrs(parent_did), + AttributeKind::Inline(InlineAttr::Force { attr_span, .. }, _) => *attr_span + ) && is_coro { - self.dcx().emit_err(errors::RustcForceInlineCoro { - attr_span: attr.span(), - span: parent_span, - }); + self.dcx() + .emit_err(errors::RustcForceInlineCoro { attr_span, span: parent_span }); } } (Target::Fn, _) => (), - (_, Some(attr)) => { - self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span(), span }); + (_, Some(attr_span)) => { + self.dcx().emit_err(errors::RustcForceInline { attr_span, span }); } (_, None) => (), } } + fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) { + if let Some(export_name_span) = find_attr!(attrs, AttributeKind::ExportName { span: export_name_span, .. } => *export_name_span) + && let Some(no_mangle_span) = + find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span) + { + let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 { + "#[unsafe(no_mangle)]" + } else { + "#[no_mangle]" + }; + let export_name_attr = if export_name_span.edition() >= Edition::Edition2024 { + "#[unsafe(export_name)]" + } else { + "#[export_name]" + }; + + self.tcx.emit_node_span_lint( + lint::builtin::UNUSED_ATTRIBUTES, + hir_id, + no_mangle_span, + errors::MixedExportNameAndNoMangle { + no_mangle_span, + export_name_span, + no_mangle_attr, + export_name_attr, + }, + ); + } + } + /// Checks if `#[autodiff]` is applied to an item other than a function item. fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) { debug!("check_autodiff"); @@ -2671,6 +2642,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } } + + fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) { + let node_span = self.tcx.hir_span(hir_id); + + if !matches!(target, Target::Expression) { + self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span }); + return; + } + + if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) { + self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span }); + }; + } + + fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) { + let node_span = self.tcx.hir_span(hir_id); + + if !matches!(target, Target::Expression) { + self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span }); + return; + } + + if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) { + self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span }); + }; + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { @@ -2875,10 +2872,9 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { let attrs = tcx.hir_attrs(item.hir_id()); - for attr in attrs { - if attr.has_name(sym::inline) { - tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() }); - } + if let Some(attr_span) = find_attr!(attrs, AttributeKind::Inline(i, span) if !matches!(i, InlineAttr::Force{..}) => *span) + { + tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span }); } } @@ -2898,6 +2894,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_attrs, ..*providers }; } +// FIXME(jdonszelmann): remove, check during parsing fn check_duplicates( tcx: TyCtxt<'_>, attr: &Attribute, diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index b9a3849f32f..f8f489d7d06 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -53,11 +53,11 @@ impl<'tcx> ExportableItemCollector<'tcx> { let is_pub = visibilities.is_directly_public(def_id); if has_attr && !is_pub { - let vis = visibilities.effective_vis(def_id).cloned().unwrap_or( + let vis = visibilities.effective_vis(def_id).cloned().unwrap_or_else(|| { EffectiveVisibility::from_vis(Visibility::Restricted( self.tcx.parent_module_from_def_id(def_id).to_local_def_id(), - )), - ); + )) + }); let vis = vis.at_level(Level::Direct); let span = self.tcx.def_span(def_id); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e597c819a3a..ce43b2c281d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -14,7 +14,7 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind}; +use rustc_hir::{self as hir, ImplItem, ImplItemKind, Node, PatKind, QPath, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -234,7 +234,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + // Marks the ADT live if its variant appears as the pattern, + // considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`, + // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`. + // Related issue: https://github.com/rust-lang/rust/issues/120770 + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -254,7 +261,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + // Marks the ADT live if its variant appears as the pattern + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => { self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); return; @@ -359,31 +370,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return false; } - // don't ignore impls for Enums and pub Structs whose methods don't have self receiver, - // cause external crate may call such methods to construct values of these types - if let Some(local_impl_of) = impl_of.as_local() - && let Some(local_def_id) = def_id.as_local() - && let Some(fn_sig) = - self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) - && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && let TyKind::Path(QPath::Resolved(_, path)) = - self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind - && let Res::Def(def_kind, did) = path.res - { - match def_kind { - // for example, #[derive(Default)] pub struct T(i32); - // external crate can call T::default() to construct T, - // so that don't ignore impl Default for pub Enum and Structs - DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => { - return false; - } - // don't ignore impl Default for Enums, - // cause we don't know which variant is constructed - DefKind::Enum => return false, - _ => (), - }; - } - if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { @@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { impl_id: hir::ItemId, local_def_id: LocalDefId, ) -> bool { - if self.should_ignore_item(local_def_id.to_def_id()) { - return false; - } - let trait_def_id = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live - DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id, + DefKind::AssocFn => self + .tcx + .associated_item(local_def_id) + .trait_item_def_id + .and_then(|def_id| def_id.as_local()), // impl items are live if the corresponding traits are live DefKind::Impl { of_trait: true } => self .tcx .impl_trait_ref(impl_id.owner_id.def_id) - .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)), + .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()), _ => None, }; - if let Some(trait_def_id) = trait_def_id { - if let Some(trait_def_id) = trait_def_id.as_local() - && !self.live_symbols.contains(&trait_def_id) - { - return false; - } - - // FIXME: legacy logic to check whether the function may construct `Self`, - // this can be removed after supporting marking ADTs appearing in patterns - // as live, then we can check private impls of public traits directly - if let Some(fn_sig) = - self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) - && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && self.tcx.visibility(trait_def_id).is_public() - { - return true; - } + if let Some(trait_def_id) = trait_def_id + && !self.live_symbols.contains(&trait_def_id) + { + return false; } // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. @@ -635,6 +608,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { match &expr.kind { rustc_hir::PatExprKind::Path(qpath) => { + // mark the type of variant live when meeting E::V in expr + if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() { + self.check_def_id(adt.did()); + } + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); } @@ -936,7 +914,9 @@ enum ShouldWarnAboutField { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum ReportOn { + /// Report on something that hasn't got a proper name to refer to TupleField, + /// Report on something that has got a name, which could be a field but also a method NamedField, } @@ -1061,6 +1041,31 @@ impl<'tcx> DeadVisitor<'tcx> { None }; + let enum_variants_with_same_name = dead_codes + .iter() + .filter_map(|dead_item| { + if let Node::ImplItem(ImplItem { + kind: ImplItemKind::Fn(..) | ImplItemKind::Const(..), + .. + }) = tcx.hir_node_by_def_id(dead_item.def_id) + && let Some(impl_did) = tcx.opt_parent(dead_item.def_id.to_def_id()) + && let DefKind::Impl { of_trait: false } = tcx.def_kind(impl_did) + && let ty::Adt(maybe_enum, _) = tcx.type_of(impl_did).skip_binder().kind() + && maybe_enum.is_enum() + && let Some(variant) = + maybe_enum.variants().iter().find(|i| i.name == dead_item.name) + { + Some(crate::errors::EnumVariantSameName { + dead_descr: tcx.def_descr(dead_item.def_id.to_def_id()), + dead_name: dead_item.name, + variant_span: tcx.def_span(variant.def_id), + }) + } else { + None + } + }) + .collect(); + let diag = match report_on { ReportOn::TupleField => { let tuple_fields = if let Some(parent_id) = parent_item @@ -1114,6 +1119,7 @@ impl<'tcx> DeadVisitor<'tcx> { name_list, parent_info, ignored_derived_impls, + enum_variants_with_same_name, }, }; diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 17a729f422a..8f572af02c2 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -10,7 +10,7 @@ //! * Compiler internal types like `Ty` and `TyCtxt` use rustc_hir::diagnostic_items::DiagnosticItems; -use rustc_hir::{Attribute, OwnerId}; +use rustc_hir::{Attribute, CRATE_OWNER_ID, OwnerId}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, LOCAL_CRATE}; @@ -67,7 +67,7 @@ fn diagnostic_items(tcx: TyCtxt<'_>, _: LocalCrate) -> DiagnosticItems { // Collect diagnostic items in this crate. let crate_items = tcx.hir_crate_items(()); - for id in crate_items.owners() { + for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { observe_item(tcx, &mut diagnostic_items, id); } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b995781719b..f89d925202c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1,13 +1,12 @@ use std::io::Error; use std::path::{Path, PathBuf}; -use rustc_ast::Label; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, }; -use rustc_hir::{self as hir, ExprKind, Target}; +use rustc_hir::Target; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{DUMMY_SP, Span, Symbol}; @@ -32,6 +31,36 @@ pub(crate) struct AutoDiffAttr { pub attr_span: Span, } +#[derive(Diagnostic)] +#[diag(passes_loop_match_attr)] +pub(crate) struct LoopMatchAttr { + #[primary_span] + pub attr_span: Span, + #[label] + pub node_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_const_continue_attr)] +pub(crate) struct ConstContinueAttr { + #[primary_span] + pub attr_span: Span, + #[label] + pub node_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(passes_mixed_export_name_and_no_mangle)] +pub(crate) struct MixedExportNameAndNoMangle { + #[label] + #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + pub no_mangle_span: Span, + #[note] + pub export_name_span: Span, + pub no_mangle_attr: &'static str, + pub export_name_attr: &'static str, +} + #[derive(LintDiagnostic)] #[diag(passes_outer_crate_level_attr)] pub(crate) struct OuterCrateLevelAttr; @@ -613,13 +642,6 @@ pub(crate) struct UsedStatic { } #[derive(Diagnostic)] -#[diag(passes_used_compiler_linker)] -pub(crate) struct UsedCompilerLinker { - #[primary_span] - pub spans: Vec<Span>, -} - -#[derive(Diagnostic)] #[diag(passes_allow_internal_unstable)] pub(crate) struct AllowInternalUnstable { #[primary_span] @@ -1071,142 +1093,6 @@ pub(crate) struct FeaturePreviouslyDeclared<'a> { pub prev_declared: &'a str, } -pub(crate) 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, G: EmissionGuarantee> Diagnostic<'_, G> for BreakNonLoop<'a> { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::passes_break_non_loop); - diag.span(self.span); - diag.code(E0571); - diag.arg("kind", self.kind); - diag.span_label(self.span, fluent::passes_label); - if let Some(head) = self.head { - diag.span_label(head, fluent::passes_label2); - } - diag.span_suggestion( - self.span, - fluent::passes_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.downgrade_to_delayed_bug(); - } - _ => { - diag.span_suggestion( - self.break_expr_span, - fluent::passes_break_expr_suggestion, - label.ident, - Applicability::MaybeIncorrect, - ); - } - } - } - diag - } -} - -#[derive(Diagnostic)] -#[diag(passes_continue_labeled_block, code = E0696)] -pub(crate) struct ContinueLabeledBlock { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_block_label)] - pub block_span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_break_inside_closure, code = E0267)] -pub(crate) struct BreakInsideClosure<'a> { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_closure_label)] - pub closure_span: Span, - pub name: &'a str, -} - -#[derive(Diagnostic)] -#[diag(passes_break_inside_coroutine, code = E0267)] -pub(crate) struct BreakInsideCoroutine<'a> { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_coroutine_label)] - pub coroutine_span: Span, - pub name: &'a str, - pub kind: &'a str, - pub source: &'a str, -} - -#[derive(Diagnostic)] -#[diag(passes_outside_loop, code = E0268)] -pub(crate) struct OutsideLoop<'a> { - #[primary_span] - #[label] - pub spans: Vec<Span>, - pub name: &'a str, - pub is_break: bool, - #[subdiagnostic] - pub suggestion: Option<OutsideLoopSuggestion>, -} -#[derive(Subdiagnostic)] -#[multipart_suggestion(passes_outside_loop_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct OutsideLoopSuggestion { - #[suggestion_part(code = "'block: ")] - pub block_span: Span, - #[suggestion_part(code = " 'block")] - pub break_spans: Vec<Span>, -} - -#[derive(Diagnostic)] -#[diag(passes_unlabeled_in_labeled_block, code = E0695)] -pub(crate) 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(crate) struct UnlabeledCfInWhileCondition<'a> { - #[primary_span] - #[label] - pub span: Span, - pub cf_type: &'a str, -} - -#[derive(Diagnostic)] -#[diag(passes_naked_functions_incompatible_attribute, code = E0736)] -pub(crate) struct NakedFunctionIncompatibleAttribute { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_naked_attribute)] - pub naked_span: Span, - pub attr: String, -} - #[derive(Diagnostic)] #[diag(passes_attr_only_in_functions)] pub(crate) struct AttrOnlyInFunctions { @@ -1434,13 +1320,6 @@ pub(crate) enum AttrApplication { #[label] span: Span, }, - #[diag(passes_attr_application_struct_enum_function_method_union, code = E0517)] - StructEnumFunctionMethodUnion { - #[primary_span] - hint_span: Span, - #[label] - span: Span, - }, } #[derive(Diagnostic)] @@ -1561,6 +1440,15 @@ pub(crate) struct UnknownFeature { } #[derive(Diagnostic)] +#[diag(passes_unknown_feature_alias, code = E0635)] +pub(crate) struct RenamedFeature { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub alias: Symbol, +} + +#[derive(Diagnostic)] #[diag(passes_implied_feature_not_exist)] pub(crate) struct ImpliedFeatureNotExist { #[primary_span] @@ -1604,6 +1492,9 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + // only on DeadCodes since it's never a problem for tuple struct fields + enum_variants_with_same_name: Vec<EnumVariantSameName<'tcx>>, + #[subdiagnostic] parent_info: Option<ParentInfo<'tcx>>, #[subdiagnostic] ignored_derived_impls: Option<IgnoredDerivedImpls>, @@ -1625,6 +1516,15 @@ pub(crate) enum MultipleDeadCodes<'tcx> { } #[derive(Subdiagnostic)] +#[note(passes_enum_variant_same_name)] +pub(crate) struct EnumVariantSameName<'tcx> { + #[primary_span] + pub variant_span: Span, + pub dead_name: Symbol, + pub dead_descr: &'tcx str, +} + +#[derive(Subdiagnostic)] #[label(passes_parent_info)] pub(crate) struct ParentInfo<'tcx> { pub num: usize, @@ -1819,6 +1719,7 @@ impl Subdiagnostic for UnusedVariableStringInterp { #[derive(LintDiagnostic)] #[diag(passes_unused_variable_try_ignore)] pub(crate) struct UnusedVarTryIgnore { + pub name: String, #[subdiagnostic] pub sugg: UnusedVarTryIgnoreSugg, } @@ -1921,3 +1822,26 @@ pub(crate) enum UnexportableItem<'a> { field_name: &'a str, }, } + +#[derive(Diagnostic)] +#[diag(passes_repr_align_should_be_align)] +pub(crate) struct ReprAlignShouldBeAlign { + #[primary_span] + #[help] + pub span: Span, + pub item: &'static str, +} + +#[derive(Diagnostic)] +#[diag(passes_align_should_be_repr_align)] +pub(crate) struct AlignShouldBeReprAlign { + #[primary_span] + #[suggestion( + style = "verbose", + applicability = "machine-applicable", + code = "#[repr(align({align_bytes}))]" + )] + pub span: Span, + pub item: &'static str, + pub align_bytes: u64, +} diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 6852153a2e7..40bc18939d6 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -5,7 +5,7 @@ use rustc_ast::visit::BoundKind; use rustc_ast::{self as ast, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::thousands::format_with_underscores; +use rustc_data_structures::thousands::usize_with_underscores; use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -65,16 +65,16 @@ pub fn print_hir_stats(tcx: TyCtxt<'_>) { StatCollector { tcx: Some(tcx), nodes: FxHashMap::default(), seen: FxHashSet::default() }; tcx.hir_walk_toplevel_module(&mut collector); tcx.hir_walk_attributes(&mut collector); - collector.print("HIR STATS", "hir-stats"); + collector.print(tcx, "HIR STATS", "hir-stats"); } -pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) { +pub fn print_ast_stats(tcx: TyCtxt<'_>, krate: &ast::Crate) { use rustc_ast::visit::Visitor; let mut collector = StatCollector { tcx: None, nodes: FxHashMap::default(), seen: FxHashSet::default() }; collector.visit_crate(krate); - collector.print(title, prefix); + collector.print(tcx, "POST EXPANSION AST STATS", "ast-stats"); } impl<'k> StatCollector<'k> { @@ -116,34 +116,53 @@ impl<'k> StatCollector<'k> { } } - fn print(&self, title: &str, prefix: &str) { + fn print(&self, tcx: TyCtxt<'_>, title: &str, prefix: &str) { + use std::fmt::Write; + // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut nodes: Vec<_> = self.nodes.iter().collect(); nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned())); + nodes.reverse(); // bigger items first + + let name_w = 18; + let acc_size1_w = 10; + let acc_size2_w = 8; // " (NN.N%)" + let acc_size_w = acc_size1_w + acc_size2_w; + let count_w = 14; + let item_size_w = 14; + let banner_w = name_w + acc_size_w + count_w + item_size_w; let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum(); let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum(); - eprintln!("{prefix} {title}"); - eprintln!( - "{} {:<18}{:>18}{:>14}{:>14}", - prefix, "Name", "Accumulated Size", "Count", "Item Size" + // We write all the text into a string and print it with a single + // `eprint!`. This is an attempt to minimize interleaved text if multiple + // rustc processes are printing macro-stats at the same time (e.g. with + // `RUSTFLAGS='-Zinput-stats' cargo build`). It still doesn't guarantee + // non-interleaving, though. + let mut s = String::new(); + _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); + _ = writeln!(s, "{prefix} {title}: {}", tcx.crate_name(hir::def_id::LOCAL_CRATE)); + _ = writeln!( + s, + "{prefix} {:<name_w$}{:>acc_size_w$}{:>count_w$}{:>item_size_w$}", + "Name", "Accumulated Size", "Count", "Item Size" ); - eprintln!("{prefix} ----------------------------------------------------------------"); + _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w)); let percent = |m, n| (m * 100) as f64 / n as f64; for (label, node) in nodes { let size = node.stats.accum_size(); - eprintln!( - "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}", - prefix, + _ = writeln!( + s, + "{prefix} {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}{:>item_size_w$}", label, - format_with_underscores(size), + usize_with_underscores(size), percent(size, total_size), - format_with_underscores(node.stats.count), - format_with_underscores(node.stats.size) + usize_with_underscores(node.stats.count), + usize_with_underscores(node.stats.size) ); if !node.subnodes.is_empty() { // We will soon sort, so the initial order does not matter. @@ -155,26 +174,28 @@ impl<'k> StatCollector<'k> { for (label, subnode) in subnodes { let size = subnode.accum_size(); - eprintln!( - "{} - {:<18}{:>10} ({:4.1}%){:>14}", - prefix, + _ = writeln!( + s, + "{prefix} - {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}", label, - format_with_underscores(size), + usize_with_underscores(size), percent(size, total_size), - format_with_underscores(subnode.count), + usize_with_underscores(subnode.count), ); } } } - eprintln!("{prefix} ----------------------------------------------------------------"); - eprintln!( - "{} {:<18}{:>10} {:>14}", - prefix, + _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w)); + _ = writeln!( + s, + "{prefix} {:<name_w$}{:>acc_size1_w$}{:>acc_size2_w$}{:>count_w$}", "Total", - format_with_underscores(total_size), - format_with_underscores(total_count), + usize_with_underscores(total_size), + "", + usize_with_underscores(total_count), ); - eprintln!("{prefix}"); + _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); + eprint!("{s}"); } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 1831f45a9ec..af7ecf0830c 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -29,7 +29,6 @@ mod lang_items; pub mod layout_test; mod lib_features; mod liveness; -pub mod loops; mod reachable; pub mod stability; mod upvars; @@ -45,7 +44,6 @@ pub fn provide(providers: &mut Providers) { entry::provide(providers); lang_items::provide(providers); lib_features::provide(providers); - loops::provide(providers); liveness::provide(providers); reachable::provide(providers); stability::provide(providers); diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index b3e6ee9512c..127e0df1332 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -40,7 +40,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }; let feature_stability = match level { - StabilityLevel::Unstable { .. } => FeatureStability::Unstable, + StabilityLevel::Unstable { old_name, .. } => FeatureStability::Unstable { old_name }, StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since { StableSince::Version(v) => Symbol::intern(&v.to_string()), StableSince::Current => sym::env_CFG_RELEASE, @@ -71,7 +71,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }); } } - (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable, _))) => { + (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable { .. }, _))) => { self.tcx.dcx().emit_err(FeaturePreviouslyDeclared { span, feature, @@ -79,7 +79,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { prev_declared: "unstable", }); } - (FeatureStability::Unstable, Some((FeatureStability::AcceptedSince(_), _))) => { + (FeatureStability::Unstable { .. }, Some((FeatureStability::AcceptedSince(_), _))) => { self.tcx.dcx().emit_err(FeaturePreviouslyDeclared { span, feature, @@ -88,7 +88,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { }); } // duplicate `unstable` feature is ok. - (FeatureStability::Unstable, Some((FeatureStability::Unstable, _))) => {} + (FeatureStability::Unstable { .. }, Some((FeatureStability::Unstable { .. }, _))) => {} } } } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 763d9fda804..3088e189c20 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -85,6 +85,7 @@ use std::io; use std::io::prelude::*; use std::rc::Rc; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::*; @@ -145,7 +146,7 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { } // Don't run unused pass for #[naked] - if tcx.has_attr(def_id.to_def_id(), sym::naked) { + if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) { return; } @@ -1743,6 +1744,7 @@ impl<'tcx> Liveness<'_, 'tcx> { .map(|(_, pat_span, _)| *pat_span) .collect::<Vec<_>>(), errors::UnusedVarTryIgnore { + name: name.clone(), sugg: errors::UnusedVarTryIgnoreSugg { shorthands, non_shorthands, diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs deleted file mode 100644 index b06f16cc7bd..00000000000 --- a/compiler/rustc_passes/src/loops.rs +++ /dev/null @@ -1,402 +0,0 @@ -use std::collections::BTreeMap; -use std::fmt; - -use Context::*; -use rustc_hir as hir; -use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Destination, Node}; -use rustc_middle::hir::nested_filter; -use rustc_middle::query::Providers; -use rustc_middle::span_bug; -use rustc_middle::ty::TyCtxt; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::{BytePos, Span}; - -use crate::errors::{ - BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop, - OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock, -}; - -/// The context in which a block is encountered. -#[derive(Clone, Copy, Debug, PartialEq)] -enum Context { - Normal, - Fn, - Loop(hir::LoopSource), - Closure(Span), - Coroutine { - coroutine_span: Span, - kind: hir::CoroutineDesugaring, - source: hir::CoroutineSource, - }, - UnlabeledBlock(Span), - UnlabeledIfBlock(Span), - LabeledBlock, - /// E.g. The labeled block inside `['_'; 'block: { break 'block 1 + 2; }]`. - AnonConst, - /// E.g. `const { ... }`. - ConstBlock, -} - -#[derive(Clone)] -struct BlockInfo { - name: String, - spans: Vec<Span>, - suggs: Vec<Span>, -} - -#[derive(PartialEq)] -enum BreakContextKind { - Break, - Continue, -} - -impl fmt::Display for BreakContextKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BreakContextKind::Break => "break", - BreakContextKind::Continue => "continue", - } - .fmt(f) - } -} - -#[derive(Clone)] -struct CheckLoopVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - // Keep track of a stack of contexts, so that suggestions - // are not made for contexts where it would be incorrect, - // such as adding a label for an `if`. - // e.g. `if 'foo: {}` would be incorrect. - cx_stack: Vec<Context>, - block_breaks: BTreeMap<Span, BlockInfo>, -} - -fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - let mut check = - CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; - tcx.hir_visit_item_likes_in_module(module_def_id, &mut check); - check.report_outside_loop_error(); -} - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_loops, ..*providers }; -} - -impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> { - type NestedFilter = nested_filter::OnlyBodies; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - - fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) { - self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c)); - } - - fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) { - self.with_context(ConstBlock, |v| intravisit::walk_inline_const(v, c)); - } - - fn visit_fn( - &mut self, - fk: hir::intravisit::FnKind<'hir>, - fd: &'hir hir::FnDecl<'hir>, - b: hir::BodyId, - _: Span, - id: LocalDefId, - ) { - self.with_context(Fn, |v| intravisit::walk_fn(v, fk, fd, b, id)); - } - - fn visit_trait_item(&mut self, trait_item: &'hir hir::TraitItem<'hir>) { - self.with_context(Fn, |v| intravisit::walk_trait_item(v, trait_item)); - } - - fn visit_impl_item(&mut self, impl_item: &'hir hir::ImplItem<'hir>) { - self.with_context(Fn, |v| intravisit::walk_impl_item(v, impl_item)); - } - - fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { - match e.kind { - hir::ExprKind::If(cond, then, else_opt) => { - self.visit_expr(cond); - - let get_block = |ck_loop: &CheckLoopVisitor<'hir>, - expr: &hir::Expr<'hir>| - -> Option<&hir::Block<'hir>> { - if let hir::ExprKind::Block(b, None) = expr.kind - && matches!( - ck_loop.cx_stack.last(), - Some(&Normal) - | Some(&AnonConst) - | Some(&UnlabeledBlock(_)) - | Some(&UnlabeledIfBlock(_)) - ) - { - Some(b) - } else { - None - } - }; - - if let Some(b) = get_block(self, then) { - self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { - v.visit_block(b) - }); - } else { - self.visit_expr(then); - } - - if let Some(else_expr) = else_opt { - if let Some(b) = get_block(self, else_expr) { - self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { - v.visit_block(b) - }); - } else { - self.visit_expr(else_expr); - } - } - } - hir::ExprKind::Loop(ref b, _, source, _) => { - self.with_context(Loop(source), |v| v.visit_block(b)); - } - hir::ExprKind::Closure(&hir::Closure { - ref fn_decl, body, fn_decl_span, kind, .. - }) => { - let cx = match kind { - hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(kind, source)) => { - Coroutine { coroutine_span: fn_decl_span, kind, source } - } - _ => Closure(fn_decl_span), - }; - self.visit_fn_decl(fn_decl); - self.with_context(cx, |v| v.visit_nested_body(body)); - } - hir::ExprKind::Block(ref b, Some(_label)) => { - self.with_context(LabeledBlock, |v| v.visit_block(b)); - } - hir::ExprKind::Block(ref b, None) - if matches!(self.cx_stack.last(), Some(&Fn) | Some(&ConstBlock)) => - { - self.with_context(Normal, |v| v.visit_block(b)); - } - hir::ExprKind::Block( - ref b @ hir::Block { rules: hir::BlockCheckMode::DefaultBlock, .. }, - None, - ) if matches!( - self.cx_stack.last(), - Some(&Normal) | Some(&AnonConst) | Some(&UnlabeledBlock(_)) - ) => - { - self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b)); - } - hir::ExprKind::Break(break_label, ref opt_expr) => { - if let Some(e) = opt_expr { - self.visit_expr(e); - } - - if self.require_label_in_labeled_block(e.span, &break_label, "break") { - // If we emitted an error about an unlabeled break in a labeled - // block, we don't need any further checking for this break any more - return; - } - - let loop_id = match break_label.target_id { - Ok(loop_id) => Some(loop_id), - Err(hir::LoopIdError::OutsideLoopScope) => None, - Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.tcx.dcx().emit_err(UnlabeledCfInWhileCondition { - span: e.span, - cf_type: "break", - }); - None - } - Err(hir::LoopIdError::UnresolvedLabel) => None, - }; - - if let Some(Node::Block(_)) = loop_id.map(|id| self.tcx.hir_node(id)) { - return; - } - - if let Some(break_expr) = opt_expr { - let (head, loop_label, loop_kind) = if let Some(loop_id) = loop_id { - match self.tcx.hir_expect_expr(loop_id).kind { - hir::ExprKind::Loop(_, label, source, sp) => { - (Some(sp), label, Some(source)) - } - ref r => { - span_bug!(e.span, "break label resolved to a non-loop: {:?}", r) - } - } - } else { - (None, None, None) - }; - match loop_kind { - None | Some(hir::LoopSource::Loop) => (), - Some(kind) => { - let suggestion = format!( - "break{}", - break_label - .label - .map_or_else(String::new, |l| format!(" {}", l.ident)) - ); - self.tcx.dcx().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, - }); - } - } - } - - let sp_lo = e.span.with_lo(e.span.lo() + BytePos("break".len() as u32)); - let label_sp = match break_label.label { - Some(label) => sp_lo.with_hi(label.ident.span.hi()), - None => sp_lo.shrink_to_lo(), - }; - self.require_break_cx( - BreakContextKind::Break, - e.span, - label_sp, - self.cx_stack.len() - 1, - ); - } - hir::ExprKind::Continue(destination) => { - self.require_label_in_labeled_block(e.span, &destination, "continue"); - - match destination.target_id { - Ok(loop_id) => { - if let Node::Block(block) = self.tcx.hir_node(loop_id) { - self.tcx.dcx().emit_err(ContinueLabeledBlock { - span: e.span, - block_span: block.span, - }); - } - } - Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.tcx.dcx().emit_err(UnlabeledCfInWhileCondition { - span: e.span, - cf_type: "continue", - }); - } - Err(_) => {} - } - self.require_break_cx( - BreakContextKind::Continue, - e.span, - e.span, - self.cx_stack.len() - 1, - ) - } - _ => intravisit::walk_expr(self, e), - } - } -} - -impl<'hir> CheckLoopVisitor<'hir> { - fn with_context<F>(&mut self, cx: Context, f: F) - where - F: FnOnce(&mut CheckLoopVisitor<'hir>), - { - self.cx_stack.push(cx); - f(self); - self.cx_stack.pop(); - } - - fn require_break_cx( - &mut self, - br_cx_kind: BreakContextKind, - span: Span, - break_span: Span, - cx_pos: usize, - ) { - match self.cx_stack[cx_pos] { - LabeledBlock | Loop(_) => {} - Closure(closure_span) => { - self.tcx.dcx().emit_err(BreakInsideClosure { - span, - closure_span, - name: &br_cx_kind.to_string(), - }); - } - Coroutine { coroutine_span, kind, source } => { - let kind = match kind { - hir::CoroutineDesugaring::Async => "async", - hir::CoroutineDesugaring::Gen => "gen", - hir::CoroutineDesugaring::AsyncGen => "async gen", - }; - let source = match source { - hir::CoroutineSource::Block => "block", - hir::CoroutineSource::Closure => "closure", - hir::CoroutineSource::Fn => "function", - }; - self.tcx.dcx().emit_err(BreakInsideCoroutine { - span, - coroutine_span, - name: &br_cx_kind.to_string(), - kind, - source, - }); - } - UnlabeledBlock(block_span) - if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) => - { - let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo { - name: br_cx_kind.to_string(), - spans: vec![], - suggs: vec![], - }); - block.spans.push(span); - block.suggs.push(break_span); - } - UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => { - self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1); - } - Normal | AnonConst | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) | ConstBlock => { - self.tcx.dcx().emit_err(OutsideLoop { - spans: vec![span], - name: &br_cx_kind.to_string(), - is_break: br_cx_kind == BreakContextKind::Break, - suggestion: None, - }); - } - } - } - - fn require_label_in_labeled_block( - &self, - span: Span, - label: &Destination, - cf_type: &str, - ) -> bool { - if !span.is_desugaring(DesugaringKind::QuestionMark) - && self.cx_stack.last() == Some(&LabeledBlock) - && label.label.is_none() - { - self.tcx.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type }); - return true; - } - false - } - - fn report_outside_loop_error(&self) { - for (s, block) in &self.block_breaks { - self.tcx.dcx().emit_err(OutsideLoop { - spans: block.spans.clone(), - name: &block.name, - is_break: true, - suggestion: Some(OutsideLoopSuggestion { - block_span: *s, - break_spans: block.suggs.clone(), - }), - }); - } - } -} diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 45e26c8999a..56d9f5bf785 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -718,6 +718,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { issue: NonZero::new(27812), is_soft: false, implied_by: None, + old_name: None, }, feature: sym::rustc_private, }; @@ -1161,8 +1162,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { defined_features: &LibFeatures, all_implications: &UnordMap<Symbol, Symbol>, ) { - for (feature, since) in defined_features.to_sorted_vec() { - if let FeatureStability::AcceptedSince(since) = since + for (feature, stability) in defined_features.to_sorted_vec() { + if let FeatureStability::AcceptedSince(since) = stability && let Some(span) = remaining_lib_features.get(&feature) { // Warn if the user has enabled an already-stable lib feature. @@ -1181,6 +1182,12 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // implications from this crate. remaining_implications.remove(&feature); + if let FeatureStability::Unstable { old_name: Some(alias) } = stability { + if let Some(span) = remaining_lib_features.swap_remove(&alias) { + tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); + } + } + if remaining_lib_features.is_empty() && remaining_implications.is_empty() { break; } | 
