mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 13:04:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			166 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use rustc_hir as hir;
 | |
| use rustc_hir::{Expr, Stmt};
 | |
| use rustc_middle::ty::{Mutability, TyKind};
 | |
| use rustc_session::lint::FutureIncompatibilityReason;
 | |
| use rustc_session::{declare_lint, declare_lint_pass};
 | |
| use rustc_span::edition::Edition;
 | |
| use rustc_span::{BytePos, Span};
 | |
| 
 | |
| use crate::lints::{MutRefSugg, RefOfMutStatic};
 | |
| use crate::{LateContext, LateLintPass, LintContext};
 | |
| 
 | |
| declare_lint! {
 | |
|     /// The `static_mut_refs` lint checks for shared or mutable references
 | |
|     /// of mutable static inside `unsafe` blocks and `unsafe` functions.
 | |
|     ///
 | |
|     /// ### Example
 | |
|     ///
 | |
|     /// ```rust,edition2021
 | |
|     /// fn main() {
 | |
|     ///     static mut X: i32 = 23;
 | |
|     ///     static mut Y: i32 = 24;
 | |
|     ///
 | |
|     ///     unsafe {
 | |
|     ///         let y = &X;
 | |
|     ///         let ref x = X;
 | |
|     ///         let (x, y) = (&X, &Y);
 | |
|     ///         foo(&X);
 | |
|     ///     }
 | |
|     /// }
 | |
|     ///
 | |
|     /// unsafe fn _foo() {
 | |
|     ///     static mut X: i32 = 23;
 | |
|     ///     static mut Y: i32 = 24;
 | |
|     ///
 | |
|     ///     let y = &X;
 | |
|     ///     let ref x = X;
 | |
|     ///     let (x, y) = (&X, &Y);
 | |
|     ///     foo(&X);
 | |
|     /// }
 | |
|     ///
 | |
|     /// fn foo<'a>(_x: &'a i32) {}
 | |
|     /// ```
 | |
|     ///
 | |
|     /// {{produces}}
 | |
|     ///
 | |
|     /// ### Explanation
 | |
|     ///
 | |
|     /// Shared or mutable references of mutable static are almost always a mistake and
 | |
|     /// can lead to undefined behavior and various other problems in your code.
 | |
|     ///
 | |
|     /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny".
 | |
|     pub STATIC_MUT_REFS,
 | |
|     Warn,
 | |
|     "creating a shared reference to mutable static",
 | |
|     @future_incompatible = FutureIncompatibleInfo {
 | |
|         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
 | |
|         reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>",
 | |
|         explain_reason: false,
 | |
|     };
 | |
|     @edition Edition2024 => Deny;
 | |
| }
 | |
| 
 | |
| declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]);
 | |
| 
 | |
| impl<'tcx> LateLintPass<'tcx> for StaticMutRefs {
 | |
|     #[allow(rustc::usage_of_ty_tykind)]
 | |
|     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
 | |
|         let err_span = expr.span;
 | |
|         match expr.kind {
 | |
|             hir::ExprKind::AddrOf(borrow_kind, m, ex)
 | |
|                 if matches!(borrow_kind, hir::BorrowKind::Ref)
 | |
|                     && let Some(err_span) = path_is_static_mut(ex, err_span) =>
 | |
|             {
 | |
|                 let source_map = cx.sess().source_map();
 | |
|                 let snippet = source_map.span_to_snippet(err_span);
 | |
| 
 | |
|                 let sugg_span = if let Ok(snippet) = snippet {
 | |
|                     // ( ( &IDENT ) )
 | |
|                     // ~~~~ exclude these from the suggestion span to avoid unmatching parens
 | |
|                     let exclude_n_bytes: u32 = snippet
 | |
|                         .chars()
 | |
|                         .take_while(|ch| ch.is_whitespace() || *ch == '(')
 | |
|                         .map(|ch| ch.len_utf8() as u32)
 | |
|                         .sum();
 | |
| 
 | |
|                     err_span.with_lo(err_span.lo() + BytePos(exclude_n_bytes)).with_hi(ex.span.lo())
 | |
|                 } else {
 | |
|                     err_span.with_hi(ex.span.lo())
 | |
|                 };
 | |
| 
 | |
|                 emit_static_mut_refs(cx, err_span, sugg_span, m, !expr.span.from_expansion());
 | |
|             }
 | |
|             hir::ExprKind::MethodCall(_, e, _, _)
 | |
|                 if let Some(err_span) = path_is_static_mut(e, expr.span)
 | |
|                     && let typeck = cx.typeck_results()
 | |
|                     && let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id)
 | |
|                     && let inputs =
 | |
|                         cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()
 | |
|                     && let Some(receiver) = inputs.get(0)
 | |
|                     && let TyKind::Ref(_, _, m) = receiver.kind() =>
 | |
|             {
 | |
|                 emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false);
 | |
|             }
 | |
|             _ => {}
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
 | |
|         if let hir::StmtKind::Let(loc) = stmt.kind
 | |
|             && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
 | |
|             && let hir::ByRef::Yes(m) = ba.0
 | |
|             && let Some(init) = loc.init
 | |
|             && let Some(err_span) = path_is_static_mut(init, init.span)
 | |
|         {
 | |
|             emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> {
 | |
|     if err_span.from_expansion() {
 | |
|         err_span = expr.span;
 | |
|     }
 | |
| 
 | |
|     while let hir::ExprKind::Field(e, _) = expr.kind {
 | |
|         expr = e;
 | |
|     }
 | |
| 
 | |
|     if let hir::ExprKind::Path(qpath) = expr.kind
 | |
|         && let hir::QPath::Resolved(_, path) = qpath
 | |
|         && let hir::def::Res::Def(def_kind, _) = path.res
 | |
|         && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } =
 | |
|             def_kind
 | |
|     {
 | |
|         return Some(err_span);
 | |
|     }
 | |
|     None
 | |
| }
 | |
| 
 | |
| fn emit_static_mut_refs(
 | |
|     cx: &LateContext<'_>,
 | |
|     span: Span,
 | |
|     sugg_span: Span,
 | |
|     mutable: Mutability,
 | |
|     suggest_addr_of: bool,
 | |
| ) {
 | |
|     let (shared_label, shared_note, mut_note, sugg) = match mutable {
 | |
|         Mutability::Mut => {
 | |
|             let sugg =
 | |
|                 if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None };
 | |
|             ("mutable ", false, true, sugg)
 | |
|         }
 | |
|         Mutability::Not => {
 | |
|             let sugg =
 | |
|                 if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None };
 | |
|             ("shared ", true, false, sugg)
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     cx.emit_span_lint(
 | |
|         STATIC_MUT_REFS,
 | |
|         span,
 | |
|         RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note },
 | |
|     );
 | |
| }
 | 
