forge_lint/sol/med/
mapping_deletion.rs1use super::MappingDeletion;
2use crate::{
3 linter::{LateLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::sema::{
7 Gcx,
8 hir::{self, ExprKind},
9 ty::{Ty, TyKind},
10};
11
12declare_forge_lint!(
13 MAPPING_DELETION,
14 Severity::Med,
15 "mapping-deletion",
16 "`delete` on a value containing a mapping does not clear the mapping"
17);
18
19impl<'hir> LateLintPass<'hir> for MappingDeletion {
20 fn check_expr(
21 &mut self,
22 ctx: &LintContext,
23 gcx: Gcx<'hir>,
24 hir: &'hir hir::Hir<'hir>,
25 expr: &'hir hir::Expr<'hir>,
26 ) {
27 if let ExprKind::Delete(operand) = &expr.kind
30 && let Some(ty) = gcx.type_of_expr(operand.peel_parens().id)
31 && ty_contains_mapping(gcx, hir, ty, &mut Vec::new())
32 {
33 ctx.emit(&MAPPING_DELETION, expr.span);
34 }
35 }
36}
37
38fn ty_contains_mapping<'hir>(
45 gcx: Gcx<'hir>,
46 hir: &'hir hir::Hir<'hir>,
47 ty: Ty<'hir>,
48 seen: &mut Vec<hir::StructId>,
49) -> bool {
50 match ty.peel_refs().kind {
51 TyKind::Mapping(..) => true,
52 TyKind::Array(elem, _) | TyKind::DynArray(elem) | TyKind::Slice(elem) => {
53 ty_contains_mapping(gcx, hir, elem, seen)
54 }
55 TyKind::Struct(id) => {
56 if seen.contains(&id) {
57 return false;
58 }
59 seen.push(id);
60 hir.strukt(id)
61 .fields
62 .iter()
63 .any(|&field| ty_contains_mapping(gcx, hir, gcx.type_of_item(field.into()), seen))
64 }
65 _ => false,
66 }
67}