diff options
Diffstat (limited to 'compiler')
30 files changed, 333 insertions, 147 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 79717c969d7..941bb78c0dd 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -899,7 +899,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.session.psess, attr); + validate_attr::check_attr(&self.features, &self.session.psess, attr); } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 3e6d0311b27..0bae1bd07a2 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -126,7 +126,7 @@ impl<'tcx> BorrowSet<'tcx> { ) -> Self { let mut visitor = GatherBorrows { tcx, - body: body, + body, location_map: Default::default(), activation_map: Default::default(), local_map: Default::default(), diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index c1f75328272..8a2936c2657 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -213,7 +213,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { via(msg_old), ); - if msg_new == "" { + if msg_new.is_empty() { // If `msg_new` is empty, then this isn't a borrow of a union field. err.span_label(span, format!("{kind_new} borrow occurs here")); err.span_label(old_span, format!("{kind_old} borrow occurs here")); diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index ec7d4582a60..f2b5ddcd782 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -43,9 +43,9 @@ impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> { } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.borrows.clone_from(&self.borrows.entry_set_for_block(block)); - state.uninits.clone_from(&self.uninits.entry_set_for_block(block)); - state.ever_inits.clone_from(&self.ever_inits.entry_set_for_block(block)); + state.borrows.clone_from(self.borrows.entry_set_for_block(block)); + state.uninits.clone_from(self.uninits.entry_set_for_block(block)); + state.ever_inits.clone_from(self.ever_inits.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 67c11ff4a5b..c214c52880a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -895,7 +895,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { for alias_ty in alias_tys { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. - () } if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { if lt.ident.name == kw::Empty { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 77fb9fb4315..25a0d40218b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -519,7 +519,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } // Otherwise, let's descend into the referent types. - search_stack.push((*referent_ty, &referent_hir_ty.ty)); + search_stack.push((*referent_ty, referent_hir_ty.ty)); } // Match up something like `Foo<'1>` @@ -558,7 +558,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } (ty::RawPtr(mut_ty, _), hir::TyKind::Ptr(mut_hir_ty)) => { - search_stack.push((*mut_ty, &mut_hir_ty.ty)); + search_stack.push((*mut_ty, mut_hir_ty.ty)); } _ => { @@ -652,7 +652,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?; let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( self.infcx.tcx, - &self.upvars, + self.upvars, upvar_index, ); let region_name = self.synthesize_region_name(); @@ -717,7 +717,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { .output; span = output.span(); if let hir::FnRetTy::Return(ret) = output { - hir_ty = Some(self.get_future_inner_return_ty(*ret)); + hir_ty = Some(self.get_future_inner_return_ty(ret)); } " of async function" } @@ -958,7 +958,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { { let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( self.infcx.tcx, - &self.upvars, + self.upvars, upvar_index, ); let region_name = self.synthesize_region_name(); diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 5d7ce548469..923cf7e9405 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -114,7 +114,6 @@ pub(crate) fn compute_regions<'cx, 'tcx>( move_data, elements, upvars, - polonius_input, ); // Create the region inference context, taking ownership of the diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 9984f76e6d4..c590104978c 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -43,8 +43,8 @@ pub(crate) fn emit_facts<'tcx>( emit_universal_region_facts( all_facts, borrow_set, - &universal_regions, - &universal_region_relations, + universal_regions, + universal_region_relations, ); emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index bf1c1b1433e..c56eaaff076 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -344,7 +344,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() { let reg_var = ty::RegionVid::from_usize(reg_var_idx); - let origin = var_to_origin.get(®_var).unwrap_or_else(|| &RegionCtxt::Unknown); + let origin = var_to_origin.get(®_var).unwrap_or(&RegionCtxt::Unknown); components[scc_idx.as_usize()].insert((reg_var, *origin)); } @@ -2216,7 +2216,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // #114907 where this happens via liveness and dropck outlives results. // Therefore, we return a default value in case that happens, which should at worst emit a // suboptimal error, instead of the ICE. - self.universe_causes.get(&universe).cloned().unwrap_or_else(|| UniverseInfo::other()) + self.universe_causes.get(&universe).cloned().unwrap_or_else(UniverseInfo::other) } /// Tries to find the terminator of the loop in which the region 'r' resides. diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 51c3648d730..67e5c8352df 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -418,9 +418,7 @@ fn check_opaque_type_parameter_valid<'tcx>( let opaque_param = opaque_generics.param_at(i, tcx); let kind = opaque_param.kind.descr(); - if let Err(guar) = opaque_env.param_is_error(i) { - return Err(guar); - } + opaque_env.param_is_error(i)?; return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { ty: arg, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 8b863efad6c..b777e01f7a6 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -12,9 +12,7 @@ use rustc_mir_dataflow::ResultsCursor; use std::rc::Rc; use crate::{ - constraints::OutlivesConstraintSet, - facts::{AllFacts, AllFactsExt}, - region_infer::values::LivenessValues, + constraints::OutlivesConstraintSet, region_infer::values::LivenessValues, universal_regions::UniversalRegions, }; @@ -38,7 +36,6 @@ pub(super) fn generate<'mir, 'tcx>( elements: &Rc<DenseLocationMap>, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, - use_polonius: bool, ) { debug!("liveness::generate"); @@ -49,11 +46,8 @@ pub(super) fn generate<'mir, 'tcx>( ); let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx()); - if facts_enabled { - polonius::populate_access_facts(typeck, body, move_data); - }; + polonius::populate_access_facts(typeck, body, move_data); trace::trace( typeck, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index d8f03a07a63..a009e28a0dd 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -87,10 +87,10 @@ pub(super) fn populate_access_facts<'a, 'tcx>( body: &Body<'tcx>, move_data: &MoveData<'tcx>, ) { - debug!("populate_access_facts()"); - let location_table = typeck.borrowck_context.location_table; - if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { + debug!("populate_access_facts()"); + let location_table = typeck.borrowck_context.location_table; + let mut extractor = UseFactsExtractor { var_defined_at: &mut facts.var_defined_at, var_used_at: &mut facts.var_used_at, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 50843c602cc..359c4ea0eb1 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -217,35 +217,52 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { /// Add facts for all locals with free regions, since regions may outlive /// the function body only at certain nodes in the CFG. fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) -> Option<()> { - let drop_used = self - .cx - .typeck - .borrowck_context - .all_facts - .as_ref() - .map(|facts| facts.var_dropped_at.clone())?; + // This collect is more necessary than immediately apparent + // because these facts go into `add_drop_live_facts_for()`, + // which also writes to `all_facts`, and so this is genuinely + // a simulatneous overlapping mutable borrow. + // FIXME for future hackers: investigate whether this is + // actually necessary; these facts come from Polonius + // and probably maybe plausibly does not need to go back in. + // It may be necessary to just pick out the parts of + // `add_drop_live_facts_for()` that make sense. + let facts_to_add: Vec<_> = { + let drop_used = &self.cx.typeck.borrowck_context.all_facts.as_ref()?.var_dropped_at; + + let relevant_live_locals: FxIndexSet<_> = + relevant_live_locals.iter().copied().collect(); + + drop_used + .iter() + .filter_map(|(local, location_index)| { + let local_ty = self.cx.body.local_decls[*local].ty; + if relevant_live_locals.contains(local) || !local_ty.has_free_regions() { + return None; + } - let relevant_live_locals: FxIndexSet<_> = relevant_live_locals.iter().copied().collect(); - - let locations = IntervalSet::new(self.cx.elements.num_points()); - - for (local, location_index) in drop_used { - if !relevant_live_locals.contains(&local) { - let local_ty = self.cx.body.local_decls[local].ty; - if local_ty.has_free_regions() { let location = match self .cx .typeck .borrowck_context .location_table - .to_location(location_index) + .to_location(*location_index) { RichLocation::Start(l) => l, RichLocation::Mid(l) => l, }; - self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); - } - } + + Some((*local, local_ty, location)) + }) + .collect() + }; + + // FIXME: these locations seem to have a special meaning (e.g. everywhere, at the end, ...), but I don't know which one. Please help me rename it to something descriptive! + // Also, if this IntervalSet is used in many places, it maybe should have a newtype'd + // name with a description of what it means for future mortals passing by. + let locations = IntervalSet::new(self.cx.elements.num_points()); + + for (local, local_ty, location) in facts_to_add { + self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); } Some(()) } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c7c1b2af6a7..81bde14a82f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -133,7 +133,6 @@ pub(crate) fn type_check<'mir, 'tcx>( move_data: &MoveData<'tcx>, elements: &Rc<DenseLocationMap>, upvars: &[&ty::CapturedPlace<'tcx>], - use_polonius: bool, ) -> MirTypeckResults<'tcx> { let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body); let mut constraints = MirTypeckRegionConstraints { @@ -189,7 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>( checker.equate_inputs_and_outputs(body, universal_regions, &normalized_inputs_and_output); checker.check_signature_annotation(body); - liveness::generate(&mut checker, body, elements, flow_inits, move_data, use_polonius); + liveness::generate(&mut checker, body, elements, flow_inits, move_data); translate_outlives_facts(&mut checker); let opaque_type_values = infcx.take_opaque_types(); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a331d83aab0..26fc77c7f33 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1883,7 +1883,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut span: Option<Span> = None; while let Some(attr) = attrs.next() { rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); - validate_attr::check_attr(&self.cx.sess.psess, attr); + validate_attr::check_attr(features, &self.cx.sess.psess, attr); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 9e2756f07ed..5e83e0d27e1 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1145,10 +1145,6 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool { }) } -pub fn is_unsafe_attr(name: Symbol) -> bool { - BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe) -} - pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index fb3b7c0a127..bf429364318 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -125,7 +125,7 @@ pub use accepted::ACCEPTED_FEATURES; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name, - is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, + is_valid_for_get_attr, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::REMOVED_FEATURES; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 9dd82868adc..b3ebc0621cb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -26,10 +26,8 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{ - self, suggest_constraining_type_params, Article, Binder, IsSuggestable, Ty, TypeVisitableExt, - Upcast, -}; +use rustc_middle::ty::{self, suggest_constraining_type_params, Article, Binder}; +use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Upcast}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; @@ -1111,12 +1109,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)), ) { - err.multipart_suggestion( + // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end. + // For example: + // fn mismatch_types() -> i32 { + // match 1 { + // x => dbg!(x), + // } + // todo!() + // } + // -------------^^^^^^^- + // Don't add semicolon `;` at the end of `dbg!(x)` expr + fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool { + for (_, node) in tcx.hir().parent_iter(expr.hir_id) { + match node { + hir::Node::Block(block) => { + if let Some(ret) = block.expr + && ret.hir_id == expr.hir_id + { + continue; + } + } + hir::Node::Arm(arm) => { + if let hir::ExprKind::Block(block, _) = arm.body.kind + && let Some(ret) = block.expr + && ret.hir_id == expr.hir_id + { + return true; + } + } + hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => { + if let Some(ret) = block.expr + && ret.hir_id == expr.hir_id + { + continue; + } + } + _ => { + return false; + } + } + } + + false + } + let mut suggs = vec![(span.shrink_to_lo(), "return ".to_string())]; + if !is_in_arm(expr, self.tcx) { + suggs.push((span.shrink_to_hi(), ";".to_string())); + } + err.multipart_suggestion_verbose( "you might have meant to return this value", - vec![ - (span.shrink_to_lo(), "return ".to_string()), - (span.shrink_to_hi(), ";".to_string()), - ], + suggs, Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index fdedf2c2e6d..eac5083ffbf 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -825,6 +825,10 @@ lint_unnameable_test_items = cannot test inner items lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments +lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 290bb5173db..adb2a3275c0 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -319,6 +319,16 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span, + sugg_spans: (left, right), + } => { + lints::UnsafeAttrOutsideUnsafe { + span: attribute_name_span, + suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, + } + .decorate_lint(diag); + } BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b26d04d0618..6df3a11deb0 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2890,3 +2890,24 @@ pub struct RedundantImportVisibility { pub import_vis: String, pub max_vis: String, } + +#[derive(LintDiagnostic)] +#[diag(lint_unsafe_attr_outside_unsafe)] +pub struct UnsafeAttrOutsideUnsafe { + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1913b9d6a1c..265779c9374 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -115,6 +115,7 @@ declare_lint_pass! { UNNAMEABLE_TYPES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, + UNSAFE_ATTR_OUTSIDE_UNSAFE, UNSAFE_OP_IN_UNSAFE_FN, UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, @@ -4902,3 +4903,45 @@ declare_lint! { reference: "issue #123743 <https://github.com/rust-lang/rust/issues/123743>", }; } + +declare_lint! { + /// The `unsafe_attr_outside_unsafe` lint detects a missing unsafe keyword + /// on attributes considered unsafe. + /// + /// ### Example + /// + /// ```rust + /// #![feature(unsafe_attributes)] + /// #![warn(unsafe_attr_outside_unsafe)] + /// + /// #[no_mangle] + /// extern "C" fn foo() {} + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Some attributes (e.g. `no_mangle`, `export_name`, `link_section` -- see + /// [issue #82499] for a more complete list) are considered "unsafe" attributes. + /// An unsafe attribute must only be used inside unsafe(...). + /// + /// This lint can automatically wrap the attributes in `unsafe(...)` , but this + /// obviously cannot verify that the preconditions of the `unsafe` + /// attributes are fulfilled, so that is still up to the user. + /// + /// The lint is currently "allow" by default, but that might change in the + /// future. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [issue #82499]: https://github.com/rust-lang/rust/issues/82499 + pub UNSAFE_ATTR_OUTSIDE_UNSAFE, + Allow, + "detects unsafe attributes outside of unsafe", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #123757 <https://github.com/rust-lang/rust/issues/123757>", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b3838f915f6..f33aadfbbc8 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -691,6 +691,10 @@ pub enum BuiltinLintDiag { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, }, + UnsafeAttrOutsideUnsafe { + attribute_name_span: Span, + sugg_spans: (Span, Span), + }, AssociatedConstElidedLifetime { elided: bool, span: Span, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index efb9526eabc..f08efe60d96 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -366,6 +366,10 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment .label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item} .sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style +parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + parse_invalid_block_macro_segment = cannot use a `block` macro fragment here .label = the `block` fragment is within this context .suggestion = wrap this in another block @@ -866,6 +870,11 @@ parse_unmatched_angle_brackets = {$num_extra_brackets -> *[other] remove extra angle brackets } +parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + + parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 46e15734853..8d49887f164 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2997,3 +2997,34 @@ pub(crate) struct DotDotRangeAttribute { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_invalid_attr_unsafe)] +#[note] +pub struct InvalidAttrUnsafe { + #[primary_span] + pub span: Span, + pub name: Path, +} + +#[derive(Diagnostic)] +#[diag(parse_unsafe_attr_outside_unsafe)] +pub struct UnsafeAttrOutsideUnsafe { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 4ca52146039..bcb1131cc19 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -5,21 +5,73 @@ use crate::{errors, parse_in}; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, Safety}; use rustc_errors::{Applicability, FatalError, PResult}; -use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{ + AttributeSafety, AttributeTemplate, BuiltinAttribute, Features, BUILTIN_ATTRIBUTE_MAP, +}; use rustc_session::errors::report_lit_error; -use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, BytePos, Span, Symbol}; -pub fn check_attr(psess: &ParseSess, attr: &Attribute) { +pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) { if attr.is_doc_comment() { return; } let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); + let attr_item = attr.get_normal_item(); + + let is_unsafe_attr = attr_info.is_some_and(|attr| attr.safety == AttributeSafety::Unsafe); + + if features.unsafe_attributes { + if is_unsafe_attr { + if let ast::Safety::Default = attr_item.unsafety { + let path_span = attr_item.path.span; + + // If the `attr_item`'s span is not from a macro, then just suggest + // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the + // `unsafe(`, `)` right after and right before the opening and closing + // square bracket respectively. + let diag_span = if attr_item.span().can_be_used_for_suggestions() { + attr_item.span() + } else { + attr.span + .with_lo(attr.span.lo() + BytePos(2)) + .with_hi(attr.span.hi() - BytePos(1)) + }; + + if attr.span.at_least_rust_2024() { + psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { + span: path_span, + suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { + left: diag_span.shrink_to_lo(), + right: diag_span.shrink_to_hi(), + }, + }); + } else { + psess.buffer_lint( + UNSAFE_ATTR_OUTSIDE_UNSAFE, + path_span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span: path_span, + sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), + }, + ); + } + } + } else { + if let Safety::Unsafe(unsafe_span) = attr_item.unsafety { + psess.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_item.path.clone(), + }); + } + } + } // Check input tokens for built-in and key-value attributes. match attr_info { @@ -32,7 +84,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { } } } - _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => { + _ if let AttrArgs::Eq(..) = attr_item.args => { // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { Ok(_) => {} diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 84c71c4bed2..9a830b0f49b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -384,10 +384,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a0b3470df6d..2ed5bba85c6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,9 +10,7 @@ use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, IntoDiagArg, MultiSpan}; use rustc_errors::{DiagCtxtHandle, StashKey}; -use rustc_feature::{ - is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP, -}; +use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir}; @@ -116,8 +114,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - self.check_unsafe_attr(attr); - match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend] => { self.check_do_not_recommend(attr.span, hir_id, target) @@ -312,21 +308,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { true } - /// Checks if `unsafe()` is applied to an invalid attribute. - fn check_unsafe_attr(&self, attr: &Attribute) { - if !attr.is_doc_comment() { - let attr_item = attr.get_normal_item(); - if let ast::Safety::Unsafe(unsafe_span) = attr_item.unsafety { - if !is_unsafe_attr(attr.name_or_empty()) { - self.dcx().emit_err(errors::InvalidAttrUnsafe { - span: unsafe_span, - name: attr_item.path.clone(), - }); - } - } - } - } - /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition fn check_diagnostic_on_unimplemented( &self, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d27b94ebd22..f0596568092 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::fluent_generated as fluent; -use rustc_ast::{ast, Label}; +use rustc_ast::Label; use rustc_errors::{ codes::*, Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, @@ -863,15 +863,6 @@ pub struct InvalidAttrAtCrateLevel { pub item: Option<ItemFollowingInnerAttr>, } -#[derive(Diagnostic)] -#[diag(passes_invalid_attr_unsafe)] -#[note] -pub struct InvalidAttrUnsafe { - #[primary_span] - pub span: Span, - pub name: ast::Path, -} - #[derive(Clone, Copy)] pub struct ItemFollowingInnerAttr { pub span: Span, diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 9d79faadd61..70fcaab1847 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -148,22 +148,22 @@ def_regs! { r11: reg = ["r11", "fp"] % frame_pointer_r11, r12: reg = ["r12", "ip"] % not_thumb1, r14: reg = ["r14", "lr"] % not_thumb1, - s0: sreg, sreg_low16 = ["s0"], - s1: sreg, sreg_low16 = ["s1"], - s2: sreg, sreg_low16 = ["s2"], - s3: sreg, sreg_low16 = ["s3"], - s4: sreg, sreg_low16 = ["s4"], - s5: sreg, sreg_low16 = ["s5"], - s6: sreg, sreg_low16 = ["s6"], - s7: sreg, sreg_low16 = ["s7"], - s8: sreg, sreg_low16 = ["s8"], - s9: sreg, sreg_low16 = ["s9"], - s10: sreg, sreg_low16 = ["s10"], - s11: sreg, sreg_low16 = ["s11"], - s12: sreg, sreg_low16 = ["s12"], - s13: sreg, sreg_low16 = ["s13"], - s14: sreg, sreg_low16 = ["s14"], - s15: sreg, sreg_low16 = ["s15"], + s0: sreg_low16, sreg = ["s0"], + s1: sreg_low16, sreg = ["s1"], + s2: sreg_low16, sreg = ["s2"], + s3: sreg_low16, sreg = ["s3"], + s4: sreg_low16, sreg = ["s4"], + s5: sreg_low16, sreg = ["s5"], + s6: sreg_low16, sreg = ["s6"], + s7: sreg_low16, sreg = ["s7"], + s8: sreg_low16, sreg = ["s8"], + s9: sreg_low16, sreg = ["s9"], + s10: sreg_low16, sreg = ["s10"], + s11: sreg_low16, sreg = ["s11"], + s12: sreg_low16, sreg = ["s12"], + s13: sreg_low16, sreg = ["s13"], + s14: sreg_low16, sreg = ["s14"], + s15: sreg_low16, sreg = ["s15"], s16: sreg = ["s16"], s17: sreg = ["s17"], s18: sreg = ["s18"], @@ -180,22 +180,22 @@ def_regs! { s29: sreg = ["s29"], s30: sreg = ["s30"], s31: sreg = ["s31"], - d0: dreg, dreg_low16, dreg_low8 = ["d0"], - d1: dreg, dreg_low16, dreg_low8 = ["d1"], - d2: dreg, dreg_low16, dreg_low8 = ["d2"], - d3: dreg, dreg_low16, dreg_low8 = ["d3"], - d4: dreg, dreg_low16, dreg_low8 = ["d4"], - d5: dreg, dreg_low16, dreg_low8 = ["d5"], - d6: dreg, dreg_low16, dreg_low8 = ["d6"], - d7: dreg, dreg_low16, dreg_low8 = ["d7"], - d8: dreg, dreg_low16 = ["d8"], - d9: dreg, dreg_low16 = ["d9"], - d10: dreg, dreg_low16 = ["d10"], - d11: dreg, dreg_low16 = ["d11"], - d12: dreg, dreg_low16 = ["d12"], - d13: dreg, dreg_low16 = ["d13"], - d14: dreg, dreg_low16 = ["d14"], - d15: dreg, dreg_low16 = ["d15"], + d0: dreg_low8, dreg_low16, dreg = ["d0"], + d1: dreg_low8, dreg_low16, dreg = ["d1"], + d2: dreg_low8, dreg_low16, dreg = ["d2"], + d3: dreg_low8, dreg_low16, dreg = ["d3"], + d4: dreg_low8, dreg_low16, dreg = ["d4"], + d5: dreg_low8, dreg_low16, dreg = ["d5"], + d6: dreg_low8, dreg_low16, dreg = ["d6"], + d7: dreg_low8, dreg_low16, dreg = ["d7"], + d8: dreg_low16, dreg = ["d8"], + d9: dreg_low16, dreg = ["d9"], + d10: dreg_low16, dreg = ["d10"], + d11: dreg_low16, dreg = ["d11"], + d12: dreg_low16, dreg = ["d12"], + d13: dreg_low16, dreg = ["d13"], + d14: dreg_low16, dreg = ["d14"], + d15: dreg_low16, dreg = ["d15"], d16: dreg = ["d16"], d17: dreg = ["d17"], d18: dreg = ["d18"], @@ -212,14 +212,14 @@ def_regs! { d29: dreg = ["d29"], d30: dreg = ["d30"], d31: dreg = ["d31"], - q0: qreg, qreg_low8, qreg_low4 = ["q0"], - q1: qreg, qreg_low8, qreg_low4 = ["q1"], - q2: qreg, qreg_low8, qreg_low4 = ["q2"], - q3: qreg, qreg_low8, qreg_low4 = ["q3"], - q4: qreg, qreg_low8 = ["q4"], - q5: qreg, qreg_low8 = ["q5"], - q6: qreg, qreg_low8 = ["q6"], - q7: qreg, qreg_low8 = ["q7"], + q0: qreg_low4, qreg_low8, qreg = ["q0"], + q1: qreg_low4, qreg_low8, qreg = ["q1"], + q2: qreg_low4, qreg_low8, qreg = ["q2"], + q3: qreg_low4, qreg_low8, qreg = ["q3"], + q4: qreg_low8, qreg = ["q4"], + q5: qreg_low8, qreg = ["q5"], + q6: qreg_low8, qreg = ["q6"], + q7: qreg_low8, qreg = ["q7"], q8: qreg = ["q8"], q9: qreg = ["q9"], q10: qreg = ["q10"], |
