Repository: crytic/slither

Issue:
Exception: Unable to resolve high level call to external function member function
Similar to #589 , when defining a function member which is also a function (via a library), it seems the CFG construction fails parsing high level calls if these are marked external. This seems to be a separate issue, so this has been opened to track it individually.
```
library FunctionExtensions {
    function g(function() external f) internal { }
}

contract FunctionMemberCFG {
    using FunctionExtensions for *;

    function f() public {
        this.f.g();
    }
}
```

Output:
```
Traceback (most recent call last):
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 887, in _find_read_write_call
    self._high_level_calls.append((ir.destination.type.type, ir.function))
AttributeError: 'NoneType' object has no attribute 'type'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 610, in main_impl
    (slither_instances, results_detectors, results_printers, number_contracts) = process_all(filename, args,
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 67, in process_all
    (slither, current_results_detectors, current_results_printers, current_analyzed_count) = process_single(
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 53, in process_single
    slither = Slither(target,
  File "c:\users\x\documents\github\slither\slither\slither.py", line 86, in __init__
    self._parser.analyze_contracts()
  File "c:\users\x\documents\github\slither\slither\solc_parsing\slitherSolc.py", line 345, in analyze_contracts
    self._convert_to_slithir()
  File "c:\users\x\documents\github\slither\slither\solc_parsing\slitherSolc.py", line 489, in _convert_to_slithir
    func.generate_slithir_and_analyze()
  File "c:\users\x\documents\github\slither\slither\core\declarations\function.py", line 1652, in generate_slithir_and_analyze
    node.slithir_generation()
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 704, in slithir_generation
    self._find_read_write_call()
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 889, in _find_read_write_call
    raise SlitherException(
slither.exceptions.SlitherException: Function not found on TMP_0(None) = HIGH_LEVEL_CALL, dest:REF_0(None), function:g, arguments:[]  . Please try compiling with a recent Solidity version.
ERROR:root:Error:
ERROR:root:Function not found on TMP_0(None) = HIGH_LEVEL_CALL, dest:REF_0(None), function:g, arguments:[]  . Please try compiling with a recent Solidity version.
ERROR:root:Please report an issue to https://github.com/crytic/slither/issues
```
Exception: Unable to resolve high level call to external function member function
Similar to #589 , when defining a function member which is also a function (via a library), it seems the CFG construction fails parsing high level calls if these are marked external. This seems to be a separate issue, so this has been opened to track it individually.
```
library FunctionExtensions {
    function g(function() external f) internal { }
}

contract FunctionMemberCFG {
    using FunctionExtensions for *;

    function f() public {
        this.f.g();
    }
}
```

Output:
```
Traceback (most recent call last):
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 887, in _find_read_write_call
    self._high_level_calls.append((ir.destination.type.type, ir.function))
AttributeError: 'NoneType' object has no attribute 'type'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 610, in main_impl
    (slither_instances, results_detectors, results_printers, number_contracts) = process_all(filename, args,
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 67, in process_all
    (slither, current_results_detectors, current_results_printers, current_analyzed_count) = process_single(
  File "c:\users\x\documents\github\slither\slither\__main__.py", line 53, in process_single
    slither = Slither(target,
  File "c:\users\x\documents\github\slither\slither\slither.py", line 86, in __init__
    self._parser.analyze_contracts()
  File "c:\users\x\documents\github\slither\slither\solc_parsing\slitherSolc.py", line 345, in analyze_contracts
    self._convert_to_slithir()
  File "c:\users\x\documents\github\slither\slither\solc_parsing\slitherSolc.py", line 489, in _convert_to_slithir
    func.generate_slithir_and_analyze()
  File "c:\users\x\documents\github\slither\slither\core\declarations\function.py", line 1652, in generate_slithir_and_analyze
    node.slithir_generation()
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 704, in slithir_generation
    self._find_read_write_call()
  File "c:\users\x\documents\github\slither\slither\core\cfg\node.py", line 889, in _find_read_write_call
    raise SlitherException(
slither.exceptions.SlitherException: Function not found on TMP_0(None) = HIGH_LEVEL_CALL, dest:REF_0(None), function:g, arguments:[]  . Please try compiling with a recent Solidity version.
ERROR:root:Error:
ERROR:root:Function not found on TMP_0(None) = HIGH_LEVEL_CALL, dest:REF_0(None), function:g, arguments:[]  . Please try compiling with a recent Solidity version.
ERROR:root:Please report an issue to https://github.com/crytic/slither/issues
```


Discussion:

Resolving patch:
diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py
--- a/slither/slithir/convert.py
+++ b/slither/slithir/convert.py
@@ -10,6 +10,7 @@
                                          UserDefinedType, TypeInformation)
 from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
 from slither.core.solidity_types.type import Type
+from slither.core.variables.function_type_variable import FunctionTypeVariable
 from slither.core.variables.variable import Variable
 from slither.core.variables.state_variable import StateVariable
 from slither.slithir.operations.codesize import CodeSize
@@ -534,7 +535,30 @@ def propagate_types(ir, node):
                     return _convert_type_contract(ir, node.function.slither)
                 left = ir.variable_left
                 t = None
-                if isinstance(left, (Variable, SolidityVariable)):
+                # Handling of this.function_name usage
+                if (left == SolidityVariable("this") and
+                    isinstance(ir.variable_right, Constant) and
+                    str(ir.variable_right) in [x.name for x in ir.function.contract.functions]
+                ):
+                    # Assumption that this.function_name can only compile if
+                    # And the contract does not have two functions starting with function_name
+                    # Otherwise solc raises:
+                    # Error: Member "f" not unique after argument-dependent lookup in contract
+                    targeted_function = next((x for x in ir.function.contract.functions if
+                                              x.name == str(ir.variable_right)))
+                    parameters = []
+                    returns = []
+                    for parameter in targeted_function.parameters:
+                        v = FunctionTypeVariable()
+                        v.name = parameter.name
+                        parameters.append(v)
+                    for return_var in targeted_function.returns:
+                        v = FunctionTypeVariable()
+                        v.name = return_var.name
+                        returns.append(v)
+                    t = FunctionType(parameters, returns)
+                    ir.lvalue.set_type(t)
+                elif isinstance(left, (Variable, SolidityVariable)):
                     t = ir.variable_left.type
                 elif isinstance(left, (Contract, Enum, Structure)):
                     t = UserDefinedType(left)
@@ -967,7 +991,6 @@ def convert_to_library(ir, node, using_for):
     # Though we could use .contract as libraries cannot be shadowed
     contract = node.function.contract_declarer
     t = ir.destination.type
-
     if t in using_for:
         new_ir = look_for_library(contract, ir, node, using_for, t)
         if new_ir:


Test patch: