From 4de199405ec9f8f56622867fd74eb206c7477bf2 Mon Sep 17 00:00:00 2001 From: Pratyksh Date: Thu, 15 May 2025 02:14:05 +0530 Subject: [PATCH 1/9] fix: match Solidity behavior for memory array deletion (issue #1785) Signed-off-by: Pratyksh --- src/sema/statements.rs | 100 ++++++++++++++++++++++++++++++--- tests/polkadot_tests/arrays.rs | 84 +++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 8 deletions(-) diff --git a/src/sema/statements.rs b/src/sema/statements.rs index eead42e7f..fa996fcdc 100644 --- a/src/sema/statements.rs +++ b/src/sema/statements.rs @@ -4,7 +4,7 @@ use super::ast::*; use super::contracts::is_base; use super::diagnostics::Diagnostics; use super::expression::{ - function_call::{available_functions, call_expr, named_call_expr}, + function_call::{available_functions, call_expr}, ExprContext, ResolveTo, }; use super::symtable::Symtable; @@ -12,7 +12,7 @@ use crate::sema::expression::constructor::{ constructor_named_args, match_constructor_to_args, new, }; use crate::sema::expression::function_call::{ - function_call_expr, function_call_pos_args, named_function_call_expr, + function_call_expr, function_call_pos_args, named_call_expr, }; use crate::sema::expression::resolve_expression::expression; use crate::sema::function_annotation::function_body_annotations; @@ -29,6 +29,8 @@ use solang_parser::pt::CodeLocation; use solang_parser::pt::OptionalCodeLocation; use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::Arc; +use num_bigint::BigInt; +use num_traits::Zero; pub fn resolve_function_body( def: &pt::FunctionDefinition, @@ -713,7 +715,8 @@ fn statement( let expr = expression(expr, context, ns, symtable, diagnostics, ResolveTo::Unknown)?; used_variable(ns, &expr, symtable); - return if let Type::StorageRef(_, ty) = expr.ty() { + + if let Type::StorageRef(_, ty) = expr.ty() { if expr.ty().is_mapping() { ns.diagnostics.push(Diagnostic::error( *loc, @@ -724,15 +727,33 @@ fn statement( res.push(Statement::Delete(*loc, ty.as_ref().clone(), expr)); - Ok(true) + return Ok(true); } else { + // For memory arrays and other types, we should only delete the element + // by assigning the default value, not delete the entire array + // Issue #1785 - match Solc behavior for delete array[index] ns.diagnostics.push(Diagnostic::warning( *loc, "argument to 'delete' should be storage reference".to_string(), )); - Err(()) - }; + let expr_ty = expr.ty().clone(); + let element_ty = expr_ty.deref_any(); + + if let Ok(default_expr) = get_default_value(*loc, element_ty, ns, diagnostics) { + let assign = Expression::Assign { + loc: *loc, + ty: expr.ty(), + left: Box::new(expr), + right: Box::new(default_expr), + }; + + res.push(Statement::Expression(*loc, true, assign)); + return Ok(true); + } + + return Err(()); + } } // is it an underscore modifier statement pt::Expression::Variable(id) @@ -1796,10 +1817,11 @@ fn destructure_values( res } pt::Expression::NamedFunctionCall(loc, ty, args) => { - let res = named_function_call_expr( + let res = named_call_expr( loc, ty, args, + true, context, ns, symtable, @@ -2310,10 +2332,11 @@ fn try_catch( res } pt::Expression::NamedFunctionCall(loc, ty, args) => { - let res = named_function_call_expr( + let res = named_call_expr( loc, ty, args, + true, context, ns, symtable, @@ -2752,3 +2775,64 @@ fn try_catch( Ok((stmt, finally_reachable)) } + +/// Helper function to get the default value expression for a type +fn get_default_value( + loc: pt::Loc, + ty: &Type, + ns: &Namespace, + diagnostics: &mut Diagnostics, +) -> Result { + match ty { + Type::Bool => Ok(Expression::BoolLiteral { + loc, + value: false, + }), + Type::Uint(size) => Ok(Expression::NumberLiteral { + loc, + ty: Type::Uint(*size), + value: BigInt::zero(), + }), + Type::Int(size) => Ok(Expression::NumberLiteral { + loc, + ty: Type::Int(*size), + value: BigInt::zero(), + }), + Type::Value => Ok(Expression::NumberLiteral { + loc, + ty: Type::Value, + value: BigInt::zero(), + }), + Type::Address(_) => Ok(Expression::NumberLiteral { + loc, + ty: ty.clone(), + value: BigInt::zero(), + }), + Type::Bytes(n) => Ok(Expression::BytesLiteral { + loc, + ty: Type::Bytes(*n), + value: vec![0; *n as usize], + }), + Type::String => Ok(Expression::BytesLiteral { + loc, + ty: Type::String, + value: Vec::new(), + }), + Type::DynamicBytes => Ok(Expression::BytesLiteral { + loc, + ty: Type::DynamicBytes, + value: Vec::new(), + }), + Type::Ref(r) => get_default_value(loc, r, ns, diagnostics), + Type::StorageRef(_, r) => get_default_value(loc, r, ns, diagnostics), + // For arrays, structs, and complex types, a more involved default value would be needed + // but for now we'll return a basic default value for simple types + _ => { + diagnostics.push(Diagnostic::error( + loc, + format!("cannot create default value for type {}", ty.to_string(ns)), + )); + Err(()) + } + } +} diff --git a/tests/polkadot_tests/arrays.rs b/tests/polkadot_tests/arrays.rs index 3e340fb5d..6aca4a531 100644 --- a/tests/polkadot_tests/arrays.rs +++ b/tests/polkadot_tests/arrays.rs @@ -1627,3 +1627,87 @@ fn abi_decode_dynamic_array3() { runtime.function("decode_empty", vec![]); } + +#[test] +fn memory_array_delete() { + // Test that deleting an array element in memory only resets the element to its default value + // and doesn't delete the entire array (matches Solc behavior - issue #1785) + let mut runtime = build_solidity( + r#" + contract foo { + function test() public returns (uint ret) { + uint[] memory data = new uint[](2); + data[0] = 234; + data[1] = 123; + delete data[0]; + + // Return length of array to verify it's still 2 (not deleted) + ret = data.length; + } + } + "#, + ); + + runtime.function("test", Vec::new()); + let output = runtime.output(); + assert!(output.len() >= 4); + assert_eq!(output[0], 2); +} + +#[test] +fn memory_array_delete_element_values() { + let mut runtime = build_solidity( + r#" + contract foo { + function test() public returns (uint, uint) { + uint[] memory data = new uint[](2); + data[0] = 234; + data[1] = 123; + delete data[0]; + return (data[0], data[1]); + } + } + "#, + ); + + runtime.function("test", Vec::new()); + let output = runtime.output(); + assert!(output.len() >= 8); + assert_eq!(output[0], 0); + + let mut found_123 = false; + for i in 0..output.len()-3 { + if output[i] == 123 && output[i+1] == 0 && output[i+2] == 0 && output[i+3] == 0 { + found_123 = true; + break; + } + } + assert!(found_123, "Should find 123 in the output"); +} + +#[test] +fn memory_array_delete_assembly_length() { + // Test for issue #1785 - original case + // In Solc, after deleting elements, the array length should remain unchanged + let mut runtime = build_solidity( + r#" + contract C { + function len() public returns (uint ret) { + uint[] memory data = new uint[](2); + data[0] = 234; + data[1] = 123; + delete data[0]; + delete data[1]; + // For Polkadot, we can directly return the length property + // since array length is stored at the start of the array + ret = data.length; + } + } + "#, + ); + + runtime.function("len", Vec::new()); + let output = runtime.output(); + assert!(output.len() >= 4); + assert_eq!(output[0], 2); +} From b878bf49887ef58f1928ace951c06759dfc25ee1 Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Sun, 22 Jun 2025 19:07:25 +0530 Subject: [PATCH 2/9] fix: match solc behavior for delete on memory array elements Signed-off-by: Pratyksh Gupta --- src/codegen/statements/mod.rs | 56 ++++++++++++++--- src/sema/statements.rs | 88 ++------------------------- tests/undefined_variable_detection.rs | 23 ++++++- 3 files changed, 75 insertions(+), 92 deletions(-) diff --git a/src/codegen/statements/mod.rs b/src/codegen/statements/mod.rs index cd3a67ada..f5a677689 100644 --- a/src/codegen/statements/mod.rs +++ b/src/codegen/statements/mod.rs @@ -216,13 +216,55 @@ pub(crate) fn statement( Statement::Delete(_, ty, expr) => { let var_expr = expression(expr, cfg, contract_no, Some(func), ns, vartab, opt); - cfg.add( - vartab, - Instr::ClearStorage { - ty: ty.clone(), - storage: var_expr, - }, - ); + // Check if this is a memory array element by checking if the type is a reference + // to a non-storage type (i.e., a memory array element) + let is_memory_array_element = match &ty { + Type::Ref(_) => { + // This is a reference to memory, likely an array element + true + } + Type::StorageRef(_, _) => { + // This is a storage reference, use clear storage + false + } + _ => { + // For other types, use clear storage as fallback + false + } + }; + + if is_memory_array_element { + // For memory array elements, we need to set the element to its default value + // instead of clearing storage (which would clear the entire array) + if let Some(default_value) = ty.default(ns) { + // Create a store instruction to set the element to its default value + cfg.add( + vartab, + Instr::Store { + dest: var_expr, + data: default_value, + }, + ); + } else { + // Fallback to clear storage if no default value is available + cfg.add( + vartab, + Instr::ClearStorage { + ty: ty.clone(), + storage: var_expr, + }, + ); + } + } else { + // For non-memory array elements, use the original clear storage behavior + cfg.add( + vartab, + Instr::ClearStorage { + ty: ty.clone(), + storage: var_expr, + }, + ); + } } Statement::Break(_) => { cfg.add( diff --git a/src/sema/statements.rs b/src/sema/statements.rs index fa996fcdc..1214f7a33 100644 --- a/src/sema/statements.rs +++ b/src/sema/statements.rs @@ -29,8 +29,6 @@ use solang_parser::pt::CodeLocation; use solang_parser::pt::OptionalCodeLocation; use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::Arc; -use num_bigint::BigInt; -use num_traits::Zero; pub fn resolve_function_body( def: &pt::FunctionDefinition, @@ -732,27 +730,10 @@ fn statement( // For memory arrays and other types, we should only delete the element // by assigning the default value, not delete the entire array // Issue #1785 - match Solc behavior for delete array[index] - ns.diagnostics.push(Diagnostic::warning( - *loc, - "argument to 'delete' should be storage reference".to_string(), - )); - - let expr_ty = expr.ty().clone(); - let element_ty = expr_ty.deref_any(); - - if let Ok(default_expr) = get_default_value(*loc, element_ty, ns, diagnostics) { - let assign = Expression::Assign { - loc: *loc, - ty: expr.ty(), - left: Box::new(expr), - right: Box::new(default_expr), - }; - - res.push(Statement::Expression(*loc, true, assign)); - return Ok(true); - } - - return Err(()); + // The AST should be an accurate representation of the source code + // The actual behavior will be handled in codegen + res.push(Statement::Delete(*loc, expr.ty().clone(), expr)); + return Ok(true); } } // is it an underscore modifier statement @@ -2775,64 +2756,3 @@ fn try_catch( Ok((stmt, finally_reachable)) } - -/// Helper function to get the default value expression for a type -fn get_default_value( - loc: pt::Loc, - ty: &Type, - ns: &Namespace, - diagnostics: &mut Diagnostics, -) -> Result { - match ty { - Type::Bool => Ok(Expression::BoolLiteral { - loc, - value: false, - }), - Type::Uint(size) => Ok(Expression::NumberLiteral { - loc, - ty: Type::Uint(*size), - value: BigInt::zero(), - }), - Type::Int(size) => Ok(Expression::NumberLiteral { - loc, - ty: Type::Int(*size), - value: BigInt::zero(), - }), - Type::Value => Ok(Expression::NumberLiteral { - loc, - ty: Type::Value, - value: BigInt::zero(), - }), - Type::Address(_) => Ok(Expression::NumberLiteral { - loc, - ty: ty.clone(), - value: BigInt::zero(), - }), - Type::Bytes(n) => Ok(Expression::BytesLiteral { - loc, - ty: Type::Bytes(*n), - value: vec![0; *n as usize], - }), - Type::String => Ok(Expression::BytesLiteral { - loc, - ty: Type::String, - value: Vec::new(), - }), - Type::DynamicBytes => Ok(Expression::BytesLiteral { - loc, - ty: Type::DynamicBytes, - value: Vec::new(), - }), - Type::Ref(r) => get_default_value(loc, r, ns, diagnostics), - Type::StorageRef(_, r) => get_default_value(loc, r, ns, diagnostics), - // For arrays, structs, and complex types, a more involved default value would be needed - // but for now we'll return a basic default value for simple types - _ => { - diagnostics.push(Diagnostic::error( - loc, - format!("cannot create default value for type {}", ty.to_string(ns)), - )); - Err(()) - } - } -} diff --git a/tests/undefined_variable_detection.rs b/tests/undefined_variable_detection.rs index 0e745284e..4f63a5440 100644 --- a/tests/undefined_variable_detection.rs +++ b/tests/undefined_variable_detection.rs @@ -659,7 +659,6 @@ fn try_catch() { r = hex"ABCD"; emit StringFailure(_err); } catch (bytes memory _err) { - r = hex"ABCD"; emit BytesFailure(_err); } @@ -740,3 +739,25 @@ fn try_catch() { "Variable read before being defined" ); } + +#[test] +fn memory_array_delete() { + let file = r#" + contract C { + function len() public returns (uint ret) { + uint[] memory data = new uint[](2); + data[0] = 234; + data[1] = 123; + delete data[0]; + delete data[1]; + assembly { + ret := mload(data) + } + } + } + "#; + + let ns = parse_and_codegen(file); + let errors = ns.diagnostics.errors(); + assert_eq!(errors.len(), 0); +} From dfa099c5c2a33f5489f5ae75ae1fa44256e4eb7c Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 16:57:20 +0530 Subject: [PATCH 3/9] Add warning for delete on non-storage references (issue #1785) --- src/sema/statements.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sema/statements.rs b/src/sema/statements.rs index a6128441a..7d8a5d3b5 100644 --- a/src/sema/statements.rs +++ b/src/sema/statements.rs @@ -732,6 +732,11 @@ fn statement( // Issue #1785 - match Solc behavior for delete array[index] // The AST should be an accurate representation of the source code // The actual behavior will be handled in codegen + ns.diagnostics.push(Diagnostic::warning( + *loc, + "argument to 'delete' should be storage reference".to_string(), + )); + res.push(Statement::Delete(*loc, expr.ty().clone(), expr)); return Ok(true); } From 9f3c30e501366fe9997d3a0e3a6c424407613929 Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 16:58:49 +0530 Subject: [PATCH 4/9] Fix mutability analysis for delete statements - only treat storage references as write operations --- src/sema/mutability.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index 33eed2a87..ee7691d07 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -289,9 +289,12 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec Statement::Expression(_, _, expr) => { expr.recurse(state, read_expression); } - Statement::Delete(loc, _, _) => { - state.data_account |= DataAccountUsage::WRITE; - state.write(loc) + Statement::Delete(loc, ty, _) => { + // Only treat delete on storage references as write operations + if matches!(ty, Type::StorageRef(_, _)) { + state.data_account |= DataAccountUsage::WRITE; + state.write(loc); + } } Statement::Destructure(_, fields, expr) => { // This is either a list or internal/external function call From 7e3f37df3e8e33ac51c3b0d2f5f6ca7d227a514d Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 17:17:44 +0530 Subject: [PATCH 5/9] Fix issue #1785: Improve delete statement handling for mutability analysis and warnings --- src/sema/mutability.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index ee7691d07..a6ec680c2 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -290,9 +290,11 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec expr.recurse(state, read_expression); } Statement::Delete(loc, ty, _) => { - // Only treat delete on storage references as write operations + // Always require data account access for delete operations + state.data_account |= DataAccountUsage::WRITE; + + // For mutability analysis, only treat delete on storage references as write operations if matches!(ty, Type::StorageRef(_, _)) { - state.data_account |= DataAccountUsage::WRITE; state.write(loc); } } From d5336e0c78c058ed0781b7ea813f88511103c36f Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 17:31:18 +0530 Subject: [PATCH 6/9] Fix issue #1785: Improve delete statement handling for mutability analysis and warnings --- src/sema/mutability.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index a6ec680c2..98c0ff296 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -294,7 +294,8 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec state.data_account |= DataAccountUsage::WRITE; // For mutability analysis, only treat delete on storage references as write operations - if matches!(ty, Type::StorageRef(_, _)) { + // Delete operations on literals (like 'delete 102') should not prevent 'pure' functions + if matches!(ty, Type::StorageRef(_, _)) || ty.is_contract_storage() { state.write(loc); } } From cd003738e6be48e7bee51ec67a9a432588aa823f Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 17:35:30 +0530 Subject: [PATCH 7/9] Fix issue #1785: Treat all delete operations as write operations for mutability analysis --- src/sema/mutability.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index 98c0ff296..308d25795 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -289,15 +289,13 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec Statement::Expression(_, _, expr) => { expr.recurse(state, read_expression); } - Statement::Delete(loc, ty, _) => { + Statement::Delete(loc, _, _) => { // Always require data account access for delete operations state.data_account |= DataAccountUsage::WRITE; - // For mutability analysis, only treat delete on storage references as write operations - // Delete operations on literals (like 'delete 102') should not prevent 'pure' functions - if matches!(ty, Type::StorageRef(_, _)) || ty.is_contract_storage() { - state.write(loc); - } + // For mutability analysis, all delete operations are write operations + // Delete operations inherently modify state, so they should prevent 'pure'/'view' functions + state.write(loc); } Statement::Destructure(_, fields, expr) => { // This is either a list or internal/external function call From 24b56bd36a812aae1994dcd35cb7877a38acf996 Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Mon, 4 Aug 2025 17:46:28 +0530 Subject: [PATCH 8/9] Fix issue #1785: Implement delete operator behavior for memory arrays and improve mutability analysis --- src/sema/mutability.rs | 2 +- src/sema/statements.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index 308d25795..37b3127d2 100644 --- a/src/sema/mutability.rs +++ b/src/sema/mutability.rs @@ -289,7 +289,7 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec Statement::Expression(_, _, expr) => { expr.recurse(state, read_expression); } - Statement::Delete(loc, _, _) => { + Statement::Delete(loc, _, _expr) => { // Always require data account access for delete operations state.data_account |= DataAccountUsage::WRITE; diff --git a/src/sema/statements.rs b/src/sema/statements.rs index 7d8a5d3b5..a0accb74d 100644 --- a/src/sema/statements.rs +++ b/src/sema/statements.rs @@ -732,10 +732,14 @@ fn statement( // Issue #1785 - match Solc behavior for delete array[index] // The AST should be an accurate representation of the source code // The actual behavior will be handled in codegen - ns.diagnostics.push(Diagnostic::warning( - *loc, - "argument to 'delete' should be storage reference".to_string(), - )); + + // Only generate warning for non-array elements that are not storage references + if !expr.ty().is_mapping() && !matches!(expr.ty(), Type::Array(_, _)) { + ns.diagnostics.push(Diagnostic::warning( + *loc, + "argument to 'delete' should be storage reference".to_string(), + )); + } res.push(Statement::Delete(*loc, expr.ty().clone(), expr)); return Ok(true); From 92f053041ec669f2f236f1fa105df0566674c855 Mon Sep 17 00:00:00 2001 From: Pratyksh Gupta Date: Wed, 19 Nov 2025 16:04:36 +0530 Subject: [PATCH 9/9] AST remains accurate and No warnings for array elements Signed-off-by: Pratyksh Gupta --- src/codegen/statements/mod.rs | 64 ++++++++----------- src/sema/statements.rs | 6 +- .../polkadot/arrays/storage_delete.sol | 4 +- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/codegen/statements/mod.rs b/src/codegen/statements/mod.rs index f5a677689..1c2677b2c 100644 --- a/src/codegen/statements/mod.rs +++ b/src/codegen/statements/mod.rs @@ -218,53 +218,41 @@ pub(crate) fn statement( // Check if this is a memory array element by checking if the type is a reference // to a non-storage type (i.e., a memory array element) - let is_memory_array_element = match &ty { - Type::Ref(_) => { + match &ty { + Type::Ref(inner_ty) => { // This is a reference to memory, likely an array element - true + // For delete operations, we need to set the element to its default value + // Get the default value of the dereferenced type + if let Some(default_value) = inner_ty.default(ns) { + // Create a store instruction to set the element to its default value + cfg.add( + vartab, + Instr::Store { + dest: var_expr, + data: default_value, + }, + ); + return; + } + // If no default value available, this shouldn't happen for valid types + // but we'll fall through to clear storage as a fallback } Type::StorageRef(_, _) => { // This is a storage reference, use clear storage - false } _ => { // For other types, use clear storage as fallback - false - } - }; - - if is_memory_array_element { - // For memory array elements, we need to set the element to its default value - // instead of clearing storage (which would clear the entire array) - if let Some(default_value) = ty.default(ns) { - // Create a store instruction to set the element to its default value - cfg.add( - vartab, - Instr::Store { - dest: var_expr, - data: default_value, - }, - ); - } else { - // Fallback to clear storage if no default value is available - cfg.add( - vartab, - Instr::ClearStorage { - ty: ty.clone(), - storage: var_expr, - }, - ); } - } else { - // For non-memory array elements, use the original clear storage behavior - cfg.add( - vartab, - Instr::ClearStorage { - ty: ty.clone(), - storage: var_expr, - }, - ); } + + // For non-memory array elements (storage references and other types), use clear storage + cfg.add( + vartab, + Instr::ClearStorage { + ty: ty.clone(), + storage: var_expr, + }, + ); } Statement::Break(_) => { cfg.add( diff --git a/src/sema/statements.rs b/src/sema/statements.rs index a0accb74d..3c4888f87 100644 --- a/src/sema/statements.rs +++ b/src/sema/statements.rs @@ -733,8 +733,12 @@ fn statement( // The AST should be an accurate representation of the source code // The actual behavior will be handled in codegen + // Check if this is an array element access (subscript) or an array type + let is_array_element = matches!(expr, Expression::Subscript { .. }) + || matches!(expr.ty(), Type::Array(_, _)); + // Only generate warning for non-array elements that are not storage references - if !expr.ty().is_mapping() && !matches!(expr.ty(), Type::Array(_, _)) { + if !expr.ty().is_mapping() && !is_array_element { ns.diagnostics.push(Diagnostic::warning( *loc, "argument to 'delete' should be storage reference".to_string(), diff --git a/tests/contract_testcases/polkadot/arrays/storage_delete.sol b/tests/contract_testcases/polkadot/arrays/storage_delete.sol index 36bfcda81..4e196888f 100644 --- a/tests/contract_testcases/polkadot/arrays/storage_delete.sol +++ b/tests/contract_testcases/polkadot/arrays/storage_delete.sol @@ -7,6 +7,4 @@ } } // ---- Expect: diagnostics ---- -// warning: 3:13-24: storage variable 'bar' has never been used -// warning: 5:13-35: function can be declared 'pure' -// warning: 6:17-27: argument to 'delete' should be storage reference \ No newline at end of file +// warning: 3:13-24: storage variable 'bar' has never been used \ No newline at end of file