Merge branch 'dev' into dev-add-types

pull/1624/head
Feist Josselin 2 years ago
commit 28af1627ee
  1. 4
      .github/workflows/black.yml
  2. 5
      .github/workflows/linter.yml
  3. 3
      slither/detectors/statements/divide_before_multiply.py
  4. 7
      slither/detectors/variables/unchanged_state_variables.py
  5. 4
      slither/solc_parsing/expressions/expression_parsing.py
  6. 7
      slither/solc_parsing/solidity_types/type_parsing.py
  7. 5
      slither/solc_parsing/yul/parse_yul.py
  8. BIN
      tests/ast-parsing/compile/yul-state-constant-access.sol-0.8.16-compact.zip
  9. 12
      tests/ast-parsing/expected/yul-state-constant-access.sol-0.8.16-compact.json
  10. 25
      tests/ast-parsing/yul-state-constant-access.sol
  11. 4
      tests/detectors/immutable-states/0.4.25/immut_state_variables.sol
  12. 4
      tests/detectors/immutable-states/0.5.16/immut_state_variables.sol
  13. 4
      tests/detectors/immutable-states/0.6.11/immut_state_variables.sol
  14. 4
      tests/detectors/immutable-states/0.7.6/immut_state_variables.sol
  15. 4
      tests/detectors/immutable-states/0.8.0/immut_state_variables.sol
  16. 24
      tests/detectors/immutable-states/0.8.0/immut_state_variables.sol.0.8.0.CouldBeImmutable.json
  17. 19
      tests/src_mapping/ReferencesUserDefinedAliases.sol
  18. 19
      tests/src_mapping/ReferencesUserDefinedTypesCasting.sol
  19. 1
      tests/test_ast_parsing.py
  20. 50
      tests/test_source_mapping.py

@ -9,13 +9,15 @@ defaults:
on: on:
pull_request: pull_request:
branches: [master, dev] branches: [master, dev]
paths:
- "**/*.py"
schedule: schedule:
# run CI every day even if no PRs/merges occur # run CI every day even if no PRs/merges occur
- cron: '0 12 * * *' - cron: '0 12 * * *'
jobs: jobs:
build: build:
name: Lint Code Base name: Black
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

@ -9,13 +9,16 @@ defaults:
on: on:
pull_request: pull_request:
branches: [master, dev] branches: [master, dev]
paths:
- "**/*.py"
schedule: schedule:
# run CI every day even if no PRs/merges occur # run CI every day even if no PRs/merges occur
- cron: '0 12 * * *' - cron: '0 12 * * *'
jobs: jobs:
build: build:
name: Lint Code Base name: Pylint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

@ -78,8 +78,7 @@ def _explore(to_explore: Set[Node], f_results: List[Any], divisions: DefaultDict
node_results = [] node_results = []
for ir in node.irs: for ir in node.irs:
# check for Constant, has its not hashable (TODO: make Constant hashable) if isinstance(ir, Assignment):
if isinstance(ir, Assignment) and not isinstance(ir.rvalue, Constant):
if ir.rvalue in divisions: if ir.rvalue in divisions:
# Avoid dupplicate. We dont use set so we keep the order of the nodes # Avoid dupplicate. We dont use set so we keep the order of the nodes
if node not in divisions[ir.rvalue]: if node not in divisions[ir.rvalue]:

@ -118,8 +118,9 @@ class UnchangedStateVariables:
self.constant_candidates.append(v) self.constant_candidates.append(v)
elif ( elif (
v in constructor_variables_written or v in variables_initialized not v.type.is_dynamic
) and version.parse(self.compilation_unit.solc_version) >= version.parse( and version.parse(self.compilation_unit.solc_version)
"0.6.5" >= version.parse("0.6.5")
and (v in constructor_variables_written or v in variables_initialized)
): ):
self.immutable_candidates.append(v) self.immutable_candidates.append(v)

@ -36,6 +36,7 @@ from slither.core.expressions.binary_operation import (
from slither.core.solidity_types import ( from slither.core.solidity_types import (
ArrayType, ArrayType,
ElementaryType, ElementaryType,
UserDefinedType,
) )
from slither.solc_parsing.declarations.caller_context import CallerContextExpression from slither.solc_parsing.declarations.caller_context import CallerContextExpression
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
@ -122,7 +123,6 @@ def parse_call(
if type_conversion: if type_conversion:
type_call = parse_type(UnknownType(type_return), caller_context) type_call = parse_type(UnknownType(type_return), caller_context)
if caller_context.is_compact_ast: if caller_context.is_compact_ast:
assert len(expression["arguments"]) == 1 assert len(expression["arguments"]) == 1
expression_to_parse = expression["arguments"][0] expression_to_parse = expression["arguments"][0]
@ -143,6 +143,8 @@ def parse_call(
expression = parse_expression(expression_to_parse, caller_context) expression = parse_expression(expression_to_parse, caller_context)
t = TypeConversion(expression, type_call) t = TypeConversion(expression, type_call)
t.set_offset(src, caller_context.compilation_unit) t.set_offset(src, caller_context.compilation_unit)
if isinstance(type_call, UserDefinedType):
type_call.type.references.append(t.source_mapping)
return t return t
call_gas = None call_gas = None

@ -6,7 +6,7 @@ from slither.core.declarations.custom_error_contract import CustomErrorContract
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions.literal import Literal from slither.core.expressions.literal import Literal
from slither.core.solidity_types import TypeAlias from slither.core.solidity_types import TypeAlias, TypeAliasTopLevel, TypeAliasContract
from slither.core.solidity_types.array_type import ArrayType from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.elementary_type import ( from slither.core.solidity_types.elementary_type import (
ElementaryType, ElementaryType,
@ -199,6 +199,9 @@ def _add_type_references(type_found: Type, src: str, sl: "SlitherCompilationUnit
if isinstance(type_found, UserDefinedType): if isinstance(type_found, UserDefinedType):
type_found.type.add_reference_from_raw_source(src, sl) type_found.type.add_reference_from_raw_source(src, sl)
elif isinstance(type_found, (TypeAliasTopLevel, TypeAliasContract)):
type_found.type.add_reference_from_raw_source(src, sl)
type_found.add_reference_from_raw_source(src, sl)
# TODO: since the add of FileScope, we can probably refactor this function and makes it a lot simpler # TODO: since the add of FileScope, we can probably refactor this function and makes it a lot simpler
@ -363,6 +366,7 @@ def parse_type(
if name in renaming: if name in renaming:
name = renaming[name] name = renaming[name]
if name in user_defined_types: if name in user_defined_types:
_add_type_references(user_defined_types[name], t["src"], sl)
return user_defined_types[name] return user_defined_types[name]
type_found = _find_from_type_name( type_found = _find_from_type_name(
name, name,
@ -383,6 +387,7 @@ def parse_type(
if name in renaming: if name in renaming:
name = renaming[name] name = renaming[name]
if name in user_defined_types: if name in user_defined_types:
_add_type_references(user_defined_types[name], t["src"], sl)
return user_defined_types[name] return user_defined_types[name]
type_found = _find_from_type_name( type_found = _find_from_type_name(
name, name,

@ -792,8 +792,9 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
return Identifier(local_variable) return Identifier(local_variable)
if isinstance(parent_func, FunctionContract): if isinstance(parent_func, FunctionContract):
assert parent_func.contract # Variables must be looked from the contract declarer
state_variable = parent_func.contract.get_state_variable_from_name(name) assert parent_func.contract_declarer
state_variable = parent_func.contract_declarer.get_state_variable_from_name(name)
if state_variable: if state_variable:
return Identifier(state_variable) return Identifier(state_variable)

@ -0,0 +1,12 @@
{
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n",
"f2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: INLINE ASM 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n}\n",
"f3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n"
},
"B": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n",
"f2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: INLINE ASM 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n}\n",
"f3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n"
}
}

@ -0,0 +1,25 @@
contract A{
uint private constant a = 10;
function f() public returns(uint){
return a;
}
function f2() public returns(uint){
uint ret;
assembly{
ret := a
}
}
function f3() public returns(uint){
uint ret;
unchecked{
ret = a;
}
}
}
contract B is A{
}

@ -45,9 +45,11 @@ contract MyConc{
uint not_constant_2 = getNumber(); uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number; uint not_constant_3 = 10 + block.number;
uint not_constant_5; uint not_constant_5;
string cannote_be_immutable;
constructor(uint b) public { constructor(uint b, string memory c) public {
not_constant_5 = b; not_constant_5 = b;
cannote_be_immutable = c;
} }
function getNumber() public returns(uint){ function getNumber() public returns(uint){

@ -46,9 +46,11 @@ contract MyConc{
uint not_constant_2 = getNumber(); uint not_constant_2 = getNumber();
uint not_constant_3 = 10 + block.number; uint not_constant_3 = 10 + block.number;
uint not_constant_5; uint not_constant_5;
string cannote_be_immutable;
constructor(uint b) public { constructor(uint b, string memory c) public {
not_constant_5 = b; not_constant_5 = b;
cannote_be_immutable = c;
} }
function getNumber() public returns(uint){ function getNumber() public returns(uint){

@ -67,9 +67,11 @@ contract Good {
uint immutable should_be_immutable_3 = 10 + block.number; uint immutable should_be_immutable_3 = 10 + block.number;
B immutable should_be_immutable_4 = new B(); B immutable should_be_immutable_4 = new B();
uint immutable should_be_immutable_5; uint immutable should_be_immutable_5;
string cannote_be_immutable;
constructor(uint b) public { constructor(uint b, string memory c) public {
should_be_immutable_5 = b; should_be_immutable_5 = b;
cannote_be_immutable = c;
} }
function getNumber() public returns(uint){ function getNumber() public returns(uint){

@ -66,9 +66,11 @@ contract Good {
uint immutable should_be_immutable_3 = 10 + block.number; uint immutable should_be_immutable_3 = 10 + block.number;
B immutable should_be_immutable_4 = new B(); B immutable should_be_immutable_4 = new B();
uint immutable should_be_immutable_5; uint immutable should_be_immutable_5;
string cannote_be_immutable;
constructor(uint b) { constructor(uint b, string memory c) {
should_be_immutable_5 = b; should_be_immutable_5 = b;
cannote_be_immutable = c;
} }
function getNumber() public returns(uint){ function getNumber() public returns(uint){

@ -44,9 +44,11 @@ contract Bad {
uint should_be_immutable_2 = getNumber(); uint should_be_immutable_2 = getNumber();
uint should_be_immutable_3 = 10 + block.number; uint should_be_immutable_3 = 10 + block.number;
uint should_be_immutable_5; uint should_be_immutable_5;
string cannote_be_immutable;
constructor(uint b) { constructor(uint b, string memory c) {
should_be_immutable_5 = b; should_be_immutable_5 = b;
cannote_be_immutable = c;
} }
function getNumber() public returns(uint){ function getNumber() public returns(uint){

@ -24,7 +24,7 @@
"name": "Bad", "name": "Bad",
"source_mapping": { "source_mapping": {
"start": 718, "start": 718,
"length": 493, "length": 577,
"filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
@ -49,7 +49,9 @@
53, 53,
54, 54,
55, 55,
56 56,
57,
58
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -90,7 +92,7 @@
"name": "Bad", "name": "Bad",
"source_mapping": { "source_mapping": {
"start": 718, "start": 718,
"length": 493, "length": 577,
"filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
@ -115,7 +117,9 @@
53, 53,
54, 54,
55, 55,
56 56,
57,
58
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -156,7 +160,7 @@
"name": "Bad", "name": "Bad",
"source_mapping": { "source_mapping": {
"start": 718, "start": 718,
"length": 493, "length": 577,
"filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
@ -181,7 +185,9 @@
53, 53,
54, 54,
55, 55,
56 56,
57,
58
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
@ -222,7 +228,7 @@
"name": "Bad", "name": "Bad",
"source_mapping": { "source_mapping": {
"start": 718, "start": 718,
"length": 493, "length": 577,
"filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_relative": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
"filename_absolute": "/GENERIC_PATH", "filename_absolute": "/GENERIC_PATH",
"filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol", "filename_short": "tests/detectors/immutable-states/0.8.0/immut_state_variables.sol",
@ -247,7 +253,9 @@
53, 53,
54, 54,
55, 55,
56 56,
57,
58
], ],
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2

@ -0,0 +1,19 @@
pragma solidity 0.8.16;
type aliasTopLevel is uint;
contract C
{
type aliasContractLevel is uint;
}
contract Test
{
aliasTopLevel a;
C.aliasContractLevel b;
}
function f(aliasTopLevel, C.aliasContractLevel)
{
}

@ -0,0 +1,19 @@
pragma solidity 0.8.16;
interface A
{
function a() external;
}
contract C
{
function g(address _address) private
{
A(_address).a();
}
}
function f(address _address)
{
A(_address).a();
}

@ -443,6 +443,7 @@ ALL_TESTS = [
Test("top-level-struct-0.8.0.sol", ["0.8.0"]), Test("top-level-struct-0.8.0.sol", ["0.8.0"]),
Test("yul-top-level-0.8.0.sol", ["0.8.0"]), Test("yul-top-level-0.8.0.sol", ["0.8.0"]),
Test("complex_imports/import_aliases_issue_1319/test.sol", ["0.5.12"]), Test("complex_imports/import_aliases_issue_1319/test.sol", ["0.5.12"]),
Test("yul-state-constant-access.sol", ["0.8.16"]),
] ]
# create the output folder if needed # create the output folder if needed
try: try:

@ -81,3 +81,53 @@ def test_source_mapping():
(x.start, x.end) (x.start, x.end)
for x in slither.offset_to_implementations("tests/src_mapping/inheritance.sol", 93) for x in slither.offset_to_implementations("tests/src_mapping/inheritance.sol", 93)
} == {(17, 53), (193, 230), (129, 166)} } == {(17, 53), (193, 230), (129, 166)}
def _sort_references_lines(refs: list) -> list:
return sorted([ref.lines[0] for ref in refs])
def _test_references_user_defined_aliases():
"""
Tests if references are filled correctly for user defined aliases (declared using "type [...] is [...]" statement).
"""
solc_select.switch_global_version("0.8.16", always_install=True)
slither = Slither("tests/src_mapping/ReferencesUserDefinedAliases.sol")
alias_top_level = slither.compilation_units[0].user_defined_value_types["aliasTopLevel"]
assert len(alias_top_level.references) == 2
lines = _sort_references_lines(alias_top_level.references)
assert lines == [12, 16]
alias_contract_level = (
slither.compilation_units[0]
.contracts[0]
.file_scope.user_defined_types["C.aliasContractLevel"]
)
assert len(alias_contract_level.references) == 2
lines = _sort_references_lines(alias_contract_level.references)
assert lines == [13, 16]
def _test_references_user_defined_types_when_casting():
"""
Tests if references are filled correctly for user defined types in case of casting.
"""
solc_select.switch_global_version("0.8.16", always_install=True)
slither = Slither("tests/src_mapping/ReferencesUserDefinedTypesCasting.sol")
contracts = slither.compilation_units[0].contracts
a = contracts[0] if contracts[0].is_interface else contracts[1]
assert len(a.references) == 2
lines = _sort_references_lines(a.references)
assert lines == [12, 18]
def test_references():
"""
Tests if references list is filled correctly in the following cases:
- user defined aliases (declared using "type [...] is [...]" statement)
- user defined types in case of casting (TypeConversion expressions)
"""
_test_references_user_defined_aliases()
_test_references_user_defined_types_when_casting()

Loading…
Cancel
Save