|
| 1 | +use rustc_hir::*; |
| 2 | +use rustc_lint::{LateContext, LateLintPass}; |
| 3 | +use rustc_session::declare_lint_pass; |
| 4 | +use rustc_ast::InlineAsmOptions; |
| 5 | +use clippy_utils::diagnostics::span_lint; |
| 6 | + |
| 7 | +#[derive(Clone, Copy, PartialEq, Eq)] |
| 8 | +enum AsmMemoryOptions { |
| 9 | + NoMem, |
| 10 | + Readonly, |
| 11 | +} |
| 12 | + |
| 13 | +fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> Option<Mutability> { |
| 14 | + let asm_in_expr = match asm_op { |
| 15 | + InlineAsmOperand::In { expr, .. } => expr, |
| 16 | + InlineAsmOperand::InOut { expr, .. } => expr, |
| 17 | + InlineAsmOperand::SplitInOut { in_expr, ..} => in_expr, |
| 18 | + InlineAsmOperand::SymStatic { .. } => return Some(Mutability::Not), |
| 19 | + InlineAsmOperand::Out { .. } => return None, |
| 20 | + InlineAsmOperand::Const { .. } => return None, |
| 21 | + InlineAsmOperand::SymFn { .. } => return None, |
| 22 | + InlineAsmOperand::Label { .. } => return None, |
| 23 | + }; |
| 24 | + |
| 25 | + return match cx.typeck_results().expr_ty(asm_in_expr).kind() { |
| 26 | + rustc_middle::ty::TyKind::RawPtr(_, mutbl) => Some(*mutbl), |
| 27 | + rustc_middle::ty::TyKind::Ref(_, _, mutbl) => Some(*mutbl), |
| 28 | + _ => None, |
| 29 | + }; |
| 30 | +} |
| 31 | + |
| 32 | +fn yeet_lint(cx: &LateContext<'_>, span: rustc_span::Span, msg: &'static str) { |
| 33 | + span_lint(cx, POINTER_IN_NOMEM_ASM_BLOCK, span, msg); |
| 34 | +} |
| 35 | + |
| 36 | +fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) { |
| 37 | + let nomem = asm.options.contains(InlineAsmOptions::NOMEM); |
| 38 | + let rdonly = asm.options.contains(InlineAsmOptions::READONLY); |
| 39 | + let memopt = match (nomem, rdonly) { |
| 40 | + (false, false) => return, // nothing to check |
| 41 | + (false, true) => AsmMemoryOptions::Readonly, |
| 42 | + (true, false) => AsmMemoryOptions::NoMem, |
| 43 | + (true, true) => return, // just return, rustc should fail anyway |
| 44 | + }; |
| 45 | + |
| 46 | + for (asm_op, asm_op_span) in asm.operands { |
| 47 | + let p = match has_in_operand_pointer(cx, asm_op) { |
| 48 | + Some(p) => p, |
| 49 | + None => continue, |
| 50 | + }; |
| 51 | + |
| 52 | + let msg = match (p, memopt) { |
| 53 | + (Mutability::Not, AsmMemoryOptions::Readonly) => continue, |
| 54 | + (Mutability::Not, AsmMemoryOptions::NoMem) => "const pointer in nomem", |
| 55 | + (Mutability::Mut, AsmMemoryOptions::Readonly) => "mut pointer in readonly", |
| 56 | + (Mutability::Mut, AsmMemoryOptions::NoMem) => "mut pointer in nomem", |
| 57 | + }; |
| 58 | + |
| 59 | + yeet_lint(cx, *asm_op_span, msg); |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +declare_clippy_lint! { |
| 64 | + /// ### What it does |
| 65 | + /// |
| 66 | + /// ### Why is this bad? |
| 67 | + /// |
| 68 | + /// ### Example |
| 69 | + /// ```no_run |
| 70 | + /// // example code where clippy issues a warning |
| 71 | + /// ``` |
| 72 | + /// Use instead: |
| 73 | + /// ```no_run |
| 74 | + /// // example code which does not raise clippy warning |
| 75 | + /// ``` |
| 76 | + #[clippy::version = "1.81.0"] |
| 77 | + pub POINTER_IN_NOMEM_ASM_BLOCK, |
| 78 | + suspicious, |
| 79 | + "default lint description" |
| 80 | +} |
| 81 | + |
| 82 | +declare_lint_pass!(PointerInNomemAsmBlock => [POINTER_IN_NOMEM_ASM_BLOCK]); |
| 83 | + |
| 84 | +impl<'tcx> LateLintPass<'tcx> for PointerInNomemAsmBlock { |
| 85 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { |
| 86 | + if let ExprKind::InlineAsm(asm) = &expr.kind { |
| 87 | + check_asm(cx, asm); |
| 88 | + } |
| 89 | + } |
| 90 | +} |
0 commit comments