Repository: crytic/slither

Issue:
.pop() treated as external call
The new `.pop()` array member function in solidity ^0.5.0 is treated as an external call
.pop() treated as external call
The new `.pop()` array member function in solidity ^0.5.0 is treated as an external call


Discussion:
Hi @mikemcdonald, thank you for reporting the issue!

We indeed did not implement [`pop`](https://github.com/crytic/slither/blob/07ea324a1a3bbadf13955ce098425b722ca68539/slither/slithir/convert.py#L1034), we will add it as soon as possible.
Hi @mikemcdonald, thank you for reporting the issue!

We indeed did not implement [`pop`](https://github.com/crytic/slither/blob/07ea324a1a3bbadf13955ce098425b722ca68539/slither/slithir/convert.py#L1034), we will add it as soon as possible.

Resolving patch:
diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py
--- a/slither/slithir/convert.py
+++ b/slither/slithir/convert.py
@@ -304,8 +304,7 @@ def propagate_type_and_convert_call(result, node):
                     result.insert(idx, new_ins[0])
                     result.insert(idx+1, new_ins[1])
                     idx = idx + 1
-                else:
-                    assert len(new_ins) == 3
+                elif len(new_ins) == 3:
                     new_ins[0].set_node(ins.node)
                     new_ins[1].set_node(ins.node)
                     new_ins[2].set_node(ins.node)
@@ -314,6 +313,23 @@ def propagate_type_and_convert_call(result, node):
                     result.insert(idx+1, new_ins[1])
                     result.insert(idx+2, new_ins[2])
                     idx = idx + 2
+                else:
+                    # Pop conversion
+                    assert len(new_ins) == 6
+                    new_ins[0].set_node(ins.node)
+                    new_ins[1].set_node(ins.node)
+                    new_ins[2].set_node(ins.node)
+                    new_ins[3].set_node(ins.node)
+                    new_ins[4].set_node(ins.node)
+                    new_ins[5].set_node(ins.node)
+                    del result[idx]
+                    result.insert(idx, new_ins[0])
+                    result.insert(idx + 1, new_ins[1])
+                    result.insert(idx + 2, new_ins[2])
+                    result.insert(idx + 3, new_ins[3])
+                    result.insert(idx + 4, new_ins[4])
+                    result.insert(idx + 5, new_ins[5])
+                    idx = idx + 5
             else:
                 new_ins.set_node(ins.node)
                 result[idx] = new_ins
@@ -416,6 +432,8 @@ def propagate_types(ir, node):
                 if isinstance(t, ArrayType) or (isinstance(t, ElementaryType) and t.type == 'bytes'):
                     if ir.function_name == 'push' and len(ir.arguments) == 1:
                         return convert_to_push(ir, node)
+                    if ir.function_name == 'pop' and len(ir.arguments) == 0:
+                        return convert_to_pop(ir, node)
 
             elif isinstance(ir, Index):
                 if isinstance(ir.variable_left.type, MappingType):
@@ -727,6 +745,7 @@ def convert_to_push(ir, node):
     As a result, the function return may return a list
     """
 
+    # TODO remove Push Operator, and change this to existing operators
 
     lvalue = ir.lvalue
     if isinstance(ir.arguments[0], list):
@@ -771,6 +790,54 @@ def convert_to_push(ir, node):
 
     return ir
 
+
+def convert_to_pop(ir, node):
+    """
+    Convert pop operators
+    Return a list of 6 operations
+    """
+
+    ret = []
+
+    arr = ir.destination
+    length = ReferenceVariable(node)
+    length.set_type(ElementaryType('uint256'))
+
+    ir_length = Length(arr, length)
+    ir_length.set_expression(ir.expression)
+    ir_length.lvalue.points_to = arr
+    ret.append(ir_length)
+
+    val = TemporaryVariable(node)
+
+    ir_sub_1 = Binary(val, length, Constant("1", ElementaryType('uint256')), BinaryType.SUBTRACTION)
+    ir_sub_1.set_expression(ir.expression)
+    ret.append(ir_sub_1)
+
+    element_to_delete = ReferenceVariable(node)
+    ir_assign_element_to_delete = Index(element_to_delete, arr, val, ElementaryType('uint256'))
+    ir_length.lvalue.points_to = arr
+    element_to_delete.set_type(ElementaryType('uint256'))
+    ir_assign_element_to_delete.set_expression(ir.expression)
+    ret.append(ir_assign_element_to_delete)
+
+    ir_delete = Delete(element_to_delete, element_to_delete)
+    ir_delete.set_expression(ir.expression)
+    ret.append(ir_delete)
+
+    length_to_assign = ReferenceVariable(node)
+    length_to_assign.set_type(ElementaryType('uint256'))
+    ir_length = Length(arr, length_to_assign)
+    ir_length.set_expression(ir.expression)
+    ir_length.lvalue.points_to = arr
+    ret.append(ir_length)
+
+    ir_assign_length = Assignment(length_to_assign, val, ElementaryType('uint256'))
+    ir_assign_length.set_expression(ir.expression)
+    ret.append(ir_assign_length)
+
+    return ret
+
 def look_for_library(contract, ir, node, using_for, t):
     for destination in using_for[t]:
         lib_contract = contract.slither.get_contract_from_name(str(destination))


Test patch: