Repository: crytic/slither

Issue:
[Bug]: High level call does not always have function
### Describe the issue:

I have two somehow similar examples, in the first one I have the function on the high level call in function safeAdd. In the next example I edited the array size to be constant instead of a literal, and I got None instead of a function object

### Code example to reproduce the issue:

```solidity
library SafeMath {
    uint256 private constant twelve = 12; 
    struct A {uint256 a;}
    function add(A[twelve] storage z) internal { }
}

contract MathContract {
    uint256 private constant twelve = 12; 
    using SafeMath for SafeMath.A[12];
    SafeMath.A[12] public z;
    function safeAdd() public {
        z.add();
    }
}
```
```solidity
library SafeMath {
    uint256 private constant twelve = 12; 
    struct A {uint256 a;}
    function add(A[twelve] storage z) internal { }
}

contract MathContract {
    uint256 private constant twelve = 12; 
    using SafeMath for SafeMath.A[twelve];
    SafeMath.A[twelve] public z;
    function safeAdd() public {
        z.add();
    }
}
```

### Version:

0.10.0

### Relevant log output:

```shell
>>> from slither import Slither
>>> math = Slither('a.sol').contracts[1]
>>> math.name
'MathContract'
>>> f = math.functions[0]
>>> f.name
'safeAdd'
>>> f.nodes
[<slither.core.cfg.node.Node object at 0x7f5460aa1e50>, <slither.core.cfg.node.No
de object at 0x7f5460aa2090>]
>>> f.nodes[1]
<slither.core.cfg.node.Node object at 0x7f5460aa2090>
>>> f.nodes[1].irs
[<slither.slithir.operations.library_call.LibraryCall object at 0x7f5460a748d0>]
>>> f.nodes[1].irs[0].function
<slither.core.declarations.function_contract.FunctionContract object at 0x7f5460a
9e090>
>>> f.nodes[1].irs[0].function.name
'add'
----------------------------------------------------------------------------------
>>> from slither import Slither
>>> math = Slither('a.sol').contracts[1]
>>> math.name
'MathContract'
>>> f = math.functions[0]
>>> f.name
'safeAdd'
>>> f.nodes
[<slither.core.cfg.node.Node object at 0x7f9d6379db10>, <slither.core.cfg.node.No
de object at 0x7f9d63a47850>]
>>> f.nodes[1]
<slither.core.cfg.node.Node object at 0x7f9d63a47850>
>>> f.nodes[1].irs
[<slither.slithir.operations.high_level_call.HighLevelCall object at 0x7f9d63a376
90>]
>>> f.nodes[1].irs[0].function
>>> print(f.nodes[1].irs[0].function)
None
```



Discussion:
In case you want to investigate and try to fix, I've left some brief notes 

Both examples should result in the same type here https://github.com/crytic/slither/blob/e3dcf1ecd3e9de60da046de471c5663ab637993a/slither/solc_parsing/solidity_types/type_parsing.py#L426-L438

There could be something off in the how the keys are stored here causing it not be found
https://github.com/crytic/slither/blob/e3dcf1ecd3e9de60da046de471c5663ab637993a/slither/solc_parsing/declarations/contract.py#L268
https://github.com/crytic/slither/blob/e3dcf1ecd3e9de60da046de471c5663ab637993a/slither/solc_parsing/declarations/contract.py#L603-L605

Finally, the conversion from `HighLevelCall` to `LibraryCall` is done lazily once we propagate the type information (`propagate_type_and_convert_call`) here, and this is failing likely to do the above
https://github.com/crytic/slither/blob/e3dcf1ecd3e9de60da046de471c5663ab637993a/slither/slithir/convert.py#L627-L629

Resolving patch:
diff --git a/slither/core/solidity_types/array_type.py b/slither/core/solidity_types/array_type.py
--- a/slither/core/solidity_types/array_type.py
+++ b/slither/core/solidity_types/array_type.py
@@ -74,7 +74,7 @@ def __str__(self) -> str:
     def __eq__(self, other: Any) -> bool:
         if not isinstance(other, ArrayType):
             return False
-        return self._type == other.type and self.length == other.length
+        return self._type == other.type and self._length_value == other.length_value
 
     def __hash__(self) -> int:
         return hash(str(self))


Test patch: