mirror of https://github.com/crytic/slither
commit
f8cf5d64c7
@ -0,0 +1,8 @@ |
||||
--- |
||||
version: 2 |
||||
updates: |
||||
- package-ecosystem: "github-actions" |
||||
directory: "/" |
||||
target-branch: "dev" |
||||
schedule: |
||||
interval: "weekly" |
@ -0,0 +1,54 @@ |
||||
name: Publish to PyPI |
||||
|
||||
on: |
||||
release: |
||||
types: [published] |
||||
|
||||
jobs: |
||||
build-release: |
||||
|
||||
runs-on: ubuntu-latest |
||||
|
||||
steps: |
||||
- uses: actions/checkout@v3 |
||||
|
||||
- name: Set up Python |
||||
uses: actions/setup-python@v4 |
||||
with: |
||||
python-version: '3.x' |
||||
|
||||
- name: Build distributions |
||||
run: | |
||||
python -m pip install --upgrade pip |
||||
python -m pip install build |
||||
python -m build |
||||
- name: Upload distributions |
||||
uses: actions/upload-artifact@v3 |
||||
with: |
||||
name: slither-dists |
||||
path: dist/ |
||||
|
||||
publish: |
||||
runs-on: ubuntu-latest |
||||
environment: release |
||||
permissions: |
||||
id-token: write # For trusted publishing + codesigning. |
||||
contents: write # For attaching signing artifacts to the release. |
||||
needs: |
||||
- build-release |
||||
steps: |
||||
- name: fetch dists |
||||
uses: actions/download-artifact@v3 |
||||
with: |
||||
name: slither-dists |
||||
path: dist/ |
||||
|
||||
- name: publish |
||||
uses: pypa/gh-action-pypi-publish@v1.8.6 |
||||
|
||||
- name: sign |
||||
uses: sigstore/gh-action-sigstore-python@v1.2.3 |
||||
with: |
||||
inputs: ./dist/*.tar.gz ./dist/*.whl |
||||
release-signing-artifacts: true |
||||
bundle-only: true |
@ -1,32 +1,23 @@ |
||||
from typing import Union, TYPE_CHECKING |
||||
|
||||
from typing import TYPE_CHECKING |
||||
|
||||
from slither.core.expressions.expression import Expression |
||||
from slither.core.solidity_types.type import Type |
||||
|
||||
if TYPE_CHECKING: |
||||
from slither.core.solidity_types.elementary_type import ElementaryType |
||||
from slither.core.solidity_types.type_alias import TypeAliasTopLevel |
||||
from slither.core.solidity_types.array_type import ArrayType |
||||
|
||||
|
||||
class NewArray(Expression): |
||||
|
||||
# note: dont conserve the size of the array if provided |
||||
def __init__( |
||||
self, depth: int, array_type: Union["TypeAliasTopLevel", "ElementaryType"] |
||||
) -> None: |
||||
def __init__(self, array_type: "ArrayType") -> None: |
||||
super().__init__() |
||||
assert isinstance(array_type, Type) |
||||
self._depth: int = depth |
||||
self._array_type: Type = array_type |
||||
# pylint: disable=import-outside-toplevel |
||||
from slither.core.solidity_types.array_type import ArrayType |
||||
|
||||
@property |
||||
def array_type(self) -> Type: |
||||
return self._array_type |
||||
assert isinstance(array_type, ArrayType) |
||||
self._array_type = array_type |
||||
|
||||
@property |
||||
def depth(self) -> int: |
||||
return self._depth |
||||
def array_type(self) -> "ArrayType": |
||||
return self._array_type |
||||
|
||||
def __str__(self): |
||||
return "new " + str(self._array_type) + "[]" * self._depth |
||||
return "new " + str(self._array_type) |
||||
|
@ -0,0 +1,221 @@ |
||||
from typing import List, Set |
||||
|
||||
from slither.core.cfg.node import Node, NodeType |
||||
from slither.core.declarations import Function |
||||
from slither.core.expressions import BinaryOperation, Identifier, MemberAccess, UnaryOperation |
||||
from slither.core.solidity_types import ArrayType |
||||
from slither.core.source_mapping.source_mapping import SourceMapping |
||||
from slither.core.variables import StateVariable |
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.slithir.operations import Length, Delete, HighLevelCall |
||||
|
||||
|
||||
class CacheArrayLength(AbstractDetector): |
||||
""" |
||||
Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it. |
||||
""" |
||||
|
||||
ARGUMENT = "cache-array-length" |
||||
HELP = ( |
||||
"Detects `for` loops that use `length` member of some storage array in their loop condition and don't " |
||||
"modify it. " |
||||
) |
||||
IMPACT = DetectorClassification.OPTIMIZATION |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#cache-array-length" |
||||
|
||||
WIKI_TITLE = "Cache array length" |
||||
WIKI_DESCRIPTION = ( |
||||
"Detects `for` loops that use `length` member of some storage array in their loop condition " |
||||
"and don't modify it. " |
||||
) |
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
contract C |
||||
{ |
||||
uint[] array; |
||||
|
||||
function f() public |
||||
{ |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
// code that does not modify length of `array` |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
Since the `for` loop in `f` doesn't modify `array.length`, it is more gas efficient to cache it in some local variable and use that variable instead, like in the following example: |
||||
|
||||
```solidity |
||||
contract C |
||||
{ |
||||
uint[] array; |
||||
|
||||
function f() public |
||||
{ |
||||
uint array_length = array.length; |
||||
for (uint i = 0; i < array_length; i++) |
||||
{ |
||||
// code that does not modify length of `array` |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
""" |
||||
WIKI_RECOMMENDATION = ( |
||||
"Cache the lengths of storage arrays if they are used and not modified in `for` loops." |
||||
) |
||||
|
||||
@staticmethod |
||||
def _is_identifier_member_access_comparison(exp: BinaryOperation) -> bool: |
||||
""" |
||||
Checks whether a BinaryOperation `exp` is an operation on Identifier and MemberAccess. |
||||
""" |
||||
return ( |
||||
isinstance(exp.expression_left, Identifier) |
||||
and isinstance(exp.expression_right, MemberAccess) |
||||
) or ( |
||||
isinstance(exp.expression_left, MemberAccess) |
||||
and isinstance(exp.expression_right, Identifier) |
||||
) |
||||
|
||||
@staticmethod |
||||
def _extract_array_from_length_member_access(exp: MemberAccess) -> StateVariable: |
||||
""" |
||||
Given a member access `exp`, it returns state array which `length` member is accessed through `exp`. |
||||
If array is not a state array or its `length` member is not referenced, it returns `None`. |
||||
""" |
||||
if exp.member_name != "length": |
||||
return None |
||||
if not isinstance(exp.expression, Identifier): |
||||
return None |
||||
if not isinstance(exp.expression.value, StateVariable): |
||||
return None |
||||
if not isinstance(exp.expression.value.type, ArrayType): |
||||
return None |
||||
return exp.expression.value |
||||
|
||||
@staticmethod |
||||
def _is_loop_referencing_array_length( |
||||
node: Node, visited: Set[Node], array: StateVariable, depth: int |
||||
) -> True: |
||||
""" |
||||
For a given loop, checks if it references `array.length` at some point. |
||||
Will also return True if `array.length` is referenced but not changed. |
||||
This may potentially generate false negatives in the detector, but it was done this way because: |
||||
- situations when array `length` is referenced but not modified in loop are rare |
||||
- checking if `array.length` is indeed modified would require much more work |
||||
""" |
||||
visited.add(node) |
||||
if node.type == NodeType.STARTLOOP: |
||||
depth += 1 |
||||
if node.type == NodeType.ENDLOOP: |
||||
depth -= 1 |
||||
if depth == 0: |
||||
return False |
||||
|
||||
# Array length may change in the following situations: |
||||
# - when `push` is called |
||||
# - when `pop` is called |
||||
# - when `delete` is called on the entire array |
||||
# - when external function call is made (instructions from internal function calls are already in |
||||
# `node.all_slithir_operations()`, so we don't need to handle internal calls separately) |
||||
if node.type == NodeType.EXPRESSION: |
||||
for op in node.all_slithir_operations(): |
||||
if isinstance(op, Length) and op.value == array: |
||||
# op accesses array.length, not necessarily modifying it |
||||
return True |
||||
if isinstance(op, Delete): |
||||
# take into account only delete entire array, since delete array[i] doesn't change `array.length` |
||||
if ( |
||||
isinstance(op.expression, UnaryOperation) |
||||
and isinstance(op.expression.expression, Identifier) |
||||
and op.expression.expression.value == array |
||||
): |
||||
return True |
||||
if isinstance(op, HighLevelCall) and not op.function.view and not op.function.pure: |
||||
return True |
||||
|
||||
for son in node.sons: |
||||
if son not in visited: |
||||
if CacheArrayLength._is_loop_referencing_array_length(son, visited, array, depth): |
||||
return True |
||||
return False |
||||
|
||||
@staticmethod |
||||
def _handle_loops(nodes: List[Node], non_optimal_array_len_usages: List[SourceMapping]) -> None: |
||||
""" |
||||
For each loop, checks if it has a comparison with `length` array member and, if it has, checks whether that |
||||
array size could potentially change in that loop. |
||||
If it cannot, the loop condition is added to `non_optimal_array_len_usages`. |
||||
There may be some false negatives here - see docs for `_is_loop_referencing_array_length` for more information. |
||||
""" |
||||
for node in nodes: |
||||
if node.type == NodeType.STARTLOOP: |
||||
if_node = node.sons[0] |
||||
if if_node.type != NodeType.IFLOOP: |
||||
continue |
||||
if not isinstance(if_node.expression, BinaryOperation): |
||||
continue |
||||
exp: BinaryOperation = if_node.expression |
||||
if not CacheArrayLength._is_identifier_member_access_comparison(exp): |
||||
continue |
||||
array: StateVariable |
||||
if isinstance(exp.expression_right, MemberAccess): |
||||
array = CacheArrayLength._extract_array_from_length_member_access( |
||||
exp.expression_right |
||||
) |
||||
else: # isinstance(exp.expression_left, MemberAccess) == True |
||||
array = CacheArrayLength._extract_array_from_length_member_access( |
||||
exp.expression_left |
||||
) |
||||
if array is None: |
||||
continue |
||||
|
||||
visited: Set[Node] = set() |
||||
if not CacheArrayLength._is_loop_referencing_array_length( |
||||
if_node, visited, array, 1 |
||||
): |
||||
non_optimal_array_len_usages.append(if_node) |
||||
|
||||
@staticmethod |
||||
def _get_non_optimal_array_len_usages_for_function(f: Function) -> List[SourceMapping]: |
||||
""" |
||||
Finds non-optimal usages of array length in loop conditions in a given function. |
||||
""" |
||||
non_optimal_array_len_usages: List[SourceMapping] = [] |
||||
CacheArrayLength._handle_loops(f.nodes, non_optimal_array_len_usages) |
||||
|
||||
return non_optimal_array_len_usages |
||||
|
||||
@staticmethod |
||||
def _get_non_optimal_array_len_usages(functions: List[Function]) -> List[SourceMapping]: |
||||
""" |
||||
Finds non-optimal usages of array length in loop conditions in given functions. |
||||
""" |
||||
non_optimal_array_len_usages: List[SourceMapping] = [] |
||||
|
||||
for f in functions: |
||||
non_optimal_array_len_usages += ( |
||||
CacheArrayLength._get_non_optimal_array_len_usages_for_function(f) |
||||
) |
||||
|
||||
return non_optimal_array_len_usages |
||||
|
||||
def _detect(self): |
||||
results = [] |
||||
|
||||
non_optimal_array_len_usages = CacheArrayLength._get_non_optimal_array_len_usages( |
||||
self.compilation_unit.functions |
||||
) |
||||
for usage in non_optimal_array_len_usages: |
||||
info = [ |
||||
"Loop condition at ", |
||||
usage, |
||||
" should use cached array length instead of referencing `length` member " |
||||
"of the storage array.\n ", |
||||
] |
||||
res = self.generate_result(info) |
||||
results.append(res) |
||||
return results |
@ -0,0 +1,223 @@ |
||||
from typing import List |
||||
|
||||
from slither.core.declarations import Contract, Structure, Enum |
||||
from slither.core.declarations.using_for_top_level import UsingForTopLevel |
||||
from slither.core.solidity_types import ( |
||||
UserDefinedType, |
||||
Type, |
||||
ElementaryType, |
||||
TypeAlias, |
||||
MappingType, |
||||
ArrayType, |
||||
) |
||||
from slither.core.solidity_types.elementary_type import Uint, Int, Byte |
||||
from slither.detectors.abstract_detector import ( |
||||
AbstractDetector, |
||||
DetectorClassification, |
||||
DETECTOR_INFO, |
||||
) |
||||
from slither.utils.output import Output |
||||
|
||||
|
||||
def _is_correctly_used(type_: Type, library: Contract) -> bool: |
||||
""" |
||||
Checks if a `using library for type_` statement is used correctly (that is, does library contain any function |
||||
with type_ as the first argument). |
||||
""" |
||||
for f in library.functions: |
||||
if len(f.parameters) == 0: |
||||
continue |
||||
if f.parameters[0].type and not _implicitly_convertible_to(type_, f.parameters[0].type): |
||||
continue |
||||
return True |
||||
return False |
||||
|
||||
|
||||
def _implicitly_convertible_to(type1: Type, type2: Type) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2. |
||||
""" |
||||
if isinstance(type1, TypeAlias) or isinstance(type2, TypeAlias): |
||||
if isinstance(type1, TypeAlias) and isinstance(type2, TypeAlias): |
||||
return type1.type == type2.type |
||||
return False |
||||
|
||||
if isinstance(type1, UserDefinedType) and isinstance(type2, UserDefinedType): |
||||
if isinstance(type1.type, Contract) and isinstance(type2.type, Contract): |
||||
return _implicitly_convertible_to_for_contracts(type1.type, type2.type) |
||||
|
||||
if isinstance(type1.type, Structure) and isinstance(type2.type, Structure): |
||||
return type1.type.canonical_name == type2.type.canonical_name |
||||
|
||||
if isinstance(type1.type, Enum) and isinstance(type2.type, Enum): |
||||
return type1.type.canonical_name == type2.type.canonical_name |
||||
|
||||
if isinstance(type1, ElementaryType) and isinstance(type2, ElementaryType): |
||||
return _implicitly_convertible_to_for_elementary_types(type1, type2) |
||||
|
||||
if isinstance(type1, MappingType) and isinstance(type2, MappingType): |
||||
return _implicitly_convertible_to_for_mappings(type1, type2) |
||||
|
||||
if isinstance(type1, ArrayType) and isinstance(type2, ArrayType): |
||||
return _implicitly_convertible_to_for_arrays(type1, type2) |
||||
|
||||
return False |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_arrays(type1: ArrayType, type2: ArrayType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2. |
||||
""" |
||||
return _implicitly_convertible_to(type1.type, type2.type) |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_mappings(type1: MappingType, type2: MappingType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2. |
||||
""" |
||||
return type1.type_from == type2.type_from and type1.type_to == type2.type_to |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_elementary_types( |
||||
type1: ElementaryType, type2: ElementaryType |
||||
) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2. |
||||
""" |
||||
if type1.type == "bool" and type2.type == "bool": |
||||
return True |
||||
if type1.type == "string" and type2.type == "string": |
||||
return True |
||||
if type1.type == "bytes" and type2.type == "bytes": |
||||
return True |
||||
if type1.type == "address" and type2.type == "address": |
||||
return _implicitly_convertible_to_for_addresses(type1, type2) |
||||
if type1.type in Uint and type2.type in Uint: |
||||
return _implicitly_convertible_to_for_uints(type1, type2) |
||||
if type1.type in Int and type2.type in Int: |
||||
return _implicitly_convertible_to_for_ints(type1, type2) |
||||
if ( |
||||
type1.type != "bytes" |
||||
and type2.type != "bytes" |
||||
and type1.type in Byte |
||||
and type2.type in Byte |
||||
): |
||||
return _implicitly_convertible_to_for_bytes(type1, type2) |
||||
return False |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_bytes(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2 assuming they are both bytes. |
||||
""" |
||||
assert type1.type in Byte and type2.type in Byte |
||||
assert type1.size is not None |
||||
assert type2.size is not None |
||||
|
||||
return type1.size <= type2.size |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_addresses(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2 assuming they are both addresses. |
||||
""" |
||||
assert type1.type == "address" and type2.type == "address" |
||||
# payable attribute to be implemented; for now, always return True |
||||
return True |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_ints(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2 assuming they are both ints. |
||||
""" |
||||
assert type1.type in Int and type2.type in Int |
||||
assert type1.size is not None |
||||
assert type2.size is not None |
||||
|
||||
return type1.size <= type2.size |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_uints(type1: ElementaryType, type2: ElementaryType) -> bool: |
||||
""" |
||||
Returns True if type1 may be implicitly converted to type2 assuming they are both uints. |
||||
""" |
||||
assert type1.type in Uint and type2.type in Uint |
||||
assert type1.size is not None |
||||
assert type2.size is not None |
||||
|
||||
return type1.size <= type2.size |
||||
|
||||
|
||||
def _implicitly_convertible_to_for_contracts(contract1: Contract, contract2: Contract) -> bool: |
||||
""" |
||||
Returns True if contract1 may be implicitly converted to contract2. |
||||
""" |
||||
return contract1 == contract2 or contract2 in contract1.inheritance |
||||
|
||||
|
||||
class IncorrectUsingFor(AbstractDetector): |
||||
""" |
||||
Detector for incorrect using-for statement usage. |
||||
""" |
||||
|
||||
ARGUMENT = "incorrect-using-for" |
||||
HELP = "Detects using-for statement usage when no function from a given library matches a given type" |
||||
IMPACT = DetectorClassification.INFORMATIONAL |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage" |
||||
|
||||
WIKI_TITLE = "Incorrect usage of using-for statement" |
||||
WIKI_DESCRIPTION = ( |
||||
"In Solidity, it is possible to use libraries for certain types, by the `using-for` statement " |
||||
"(`using <library> for <type>`). However, the Solidity compiler doesn't check whether a given " |
||||
"library has at least one function matching a given type. If it doesn't, such a statement has " |
||||
"no effect and may be confusing. " |
||||
) |
||||
|
||||
# region wiki_exploit_scenario |
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
library L { |
||||
function f(bool) public pure {} |
||||
} |
||||
|
||||
using L for uint; |
||||
``` |
||||
Such a code will compile despite the fact that `L` has no function with `uint` as its first argument.""" |
||||
# endregion wiki_exploit_scenario |
||||
WIKI_RECOMMENDATION = ( |
||||
"Make sure that the libraries used in `using-for` statements have at least one function " |
||||
"matching a type used in these statements. " |
||||
) |
||||
|
||||
def _append_result( |
||||
self, results: List[Output], uf: UsingForTopLevel, type_: Type, library: Contract |
||||
) -> None: |
||||
info: DETECTOR_INFO = [ |
||||
f"using-for statement at {uf.source_mapping} is incorrect - no matching function for {type_} found in ", |
||||
library, |
||||
".\n", |
||||
] |
||||
res = self.generate_result(info) |
||||
results.append(res) |
||||
|
||||
def _detect(self) -> List[Output]: |
||||
results: List[Output] = [] |
||||
|
||||
for uf in self.compilation_unit.using_for_top_level: |
||||
# UsingForTopLevel.using_for is a dict with a single entry, which is mapped to a list of functions/libraries |
||||
# the following code extracts the type from using-for and skips using-for statements with functions |
||||
type_ = list(uf.using_for.keys())[0] |
||||
for lib_or_fcn in uf.using_for[type_]: |
||||
# checking for using-for with functions is already performed by the compiler; we only consider libraries |
||||
if isinstance(lib_or_fcn, UserDefinedType): |
||||
lib_or_fcn_type = lib_or_fcn.type |
||||
if ( |
||||
isinstance(type_, Type) |
||||
and isinstance(lib_or_fcn_type, Contract) |
||||
and not _is_correctly_used(type_, lib_or_fcn_type) |
||||
): |
||||
self._append_result(results, uf, type_, lib_or_fcn_type) |
||||
|
||||
return results |
@ -0,0 +1,35 @@ |
||||
""" |
||||
Lines of Code (LOC) printer |
||||
|
||||
Definitions: |
||||
cloc: comment lines of code containing only comments |
||||
sloc: source lines of code with no whitespace or comments |
||||
loc: all lines of code including whitespace and comments |
||||
src: source files (excluding tests and dependencies) |
||||
dep: dependency files |
||||
test: test files |
||||
""" |
||||
|
||||
from slither.printers.abstract_printer import AbstractPrinter |
||||
from slither.utils.loc import compute_loc_metrics |
||||
from slither.utils.output import Output |
||||
|
||||
|
||||
class LocPrinter(AbstractPrinter): |
||||
ARGUMENT = "loc" |
||||
HELP = """Count the total number lines of code (LOC), source lines of code (SLOC), \ |
||||
and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), \ |
||||
and test files (TEST).""" |
||||
|
||||
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#loc" |
||||
|
||||
def output(self, _filename: str) -> Output: |
||||
# compute loc metrics |
||||
loc = compute_loc_metrics(self.slither) |
||||
|
||||
table = loc.to_pretty_table() |
||||
txt = "Lines of Code \n" + str(table) |
||||
self.info(txt) |
||||
res = self.generate_output(txt) |
||||
res.add_pretty_table(table, "Code Lines") |
||||
return res |
@ -1 +1 @@ |
||||
from .read_storage import SlitherReadStorage |
||||
from .read_storage import SlitherReadStorage, RpcInfo |
||||
|
@ -0,0 +1,105 @@ |
||||
from dataclasses import dataclass |
||||
from pathlib import Path |
||||
from typing import List, Tuple |
||||
|
||||
from slither import Slither |
||||
from slither.utils.myprettytable import MyPrettyTable |
||||
from slither.utils.tests_pattern import is_test_file |
||||
|
||||
|
||||
@dataclass |
||||
class LoCInfo: |
||||
loc: int = 0 |
||||
sloc: int = 0 |
||||
cloc: int = 0 |
||||
|
||||
def total(self) -> int: |
||||
return self.loc + self.sloc + self.cloc |
||||
|
||||
|
||||
@dataclass |
||||
class LoC: |
||||
src: LoCInfo = LoCInfo() |
||||
dep: LoCInfo = LoCInfo() |
||||
test: LoCInfo = LoCInfo() |
||||
|
||||
def to_pretty_table(self) -> MyPrettyTable: |
||||
table = MyPrettyTable(["", "src", "dep", "test"]) |
||||
|
||||
table.add_row(["loc", str(self.src.loc), str(self.dep.loc), str(self.test.loc)]) |
||||
table.add_row(["sloc", str(self.src.sloc), str(self.dep.sloc), str(self.test.sloc)]) |
||||
table.add_row(["cloc", str(self.src.cloc), str(self.dep.cloc), str(self.test.cloc)]) |
||||
table.add_row( |
||||
["Total", str(self.src.total()), str(self.dep.total()), str(self.test.total())] |
||||
) |
||||
return table |
||||
|
||||
|
||||
def count_lines(contract_lines: List[str]) -> Tuple[int, int, int]: |
||||
"""Function to count and classify the lines of code in a contract. |
||||
Args: |
||||
contract_lines: list(str) representing the lines of a contract. |
||||
Returns: |
||||
tuple(int, int, int) representing (cloc, sloc, loc) |
||||
""" |
||||
multiline_comment = False |
||||
cloc = 0 |
||||
sloc = 0 |
||||
loc = 0 |
||||
|
||||
for line in contract_lines: |
||||
loc += 1 |
||||
stripped_line = line.strip() |
||||
if not multiline_comment: |
||||
if stripped_line.startswith("//"): |
||||
cloc += 1 |
||||
elif "/*" in stripped_line: |
||||
# Account for case where /* is followed by */ on the same line. |
||||
# If it is, then multiline_comment does not need to be set to True |
||||
start_idx = stripped_line.find("/*") |
||||
end_idx = stripped_line.find("*/", start_idx + 2) |
||||
if end_idx == -1: |
||||
multiline_comment = True |
||||
cloc += 1 |
||||
elif stripped_line: |
||||
sloc += 1 |
||||
else: |
||||
cloc += 1 |
||||
if "*/" in stripped_line: |
||||
multiline_comment = False |
||||
|
||||
return cloc, sloc, loc |
||||
|
||||
|
||||
def _update_lines(loc_info: LoCInfo, lines: list) -> None: |
||||
"""An internal function used to update (mutate in place) the loc_info. |
||||
|
||||
Args: |
||||
loc_info: LoCInfo to be updated |
||||
lines: list(str) representing the lines of a contract. |
||||
""" |
||||
cloc, sloc, loc = count_lines(lines) |
||||
loc_info.loc += loc |
||||
loc_info.cloc += cloc |
||||
loc_info.sloc += sloc |
||||
|
||||
|
||||
def compute_loc_metrics(slither: Slither) -> LoC: |
||||
"""Used to compute the lines of code metrics for a Slither object. |
||||
|
||||
Args: |
||||
slither: A Slither object |
||||
Returns: |
||||
A LoC object |
||||
""" |
||||
|
||||
loc = LoC() |
||||
|
||||
for filename, source_code in slither.source_code.items(): |
||||
current_lines = source_code.splitlines() |
||||
is_dep = False |
||||
if slither.crytic_compile: |
||||
is_dep = slither.crytic_compile.is_dependency(filename) |
||||
loc_type = loc.dep if is_dep else loc.test if is_test_file(Path(filename)) else loc.src |
||||
_update_lines(loc_type, current_lines) |
||||
return loc |
@ -1,12 +1,14 @@ |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
E.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#63-66) which only takes arrays by value |
||||
|
||||
|
@ -1,12 +1,14 @@ |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
E.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#63-66) which only takes arrays by value |
||||
|
||||
|
@ -1,12 +1,14 @@ |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
E.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#63-66) which only takes arrays by value |
||||
|
||||
|
@ -1,12 +1,14 @@ |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value |
||||
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value |
||||
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value |
||||
|
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value |
||||
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value |
||||
|
||||
E.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#63-66) which only takes arrays by value |
||||
|
||||
|
@ -0,0 +1,18 @@ |
||||
Loop condition at i < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#36) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at i_scope_22 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#166) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at j_scope_11 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#108) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at i_scope_6 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#79) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at i_scope_21 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#160) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at k_scope_9 < array2.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#98) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at k_scope_17 < array2.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#132) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at j_scope_15 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#125) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
||||
Loop condition at i_scope_4 < array.length (tests/e2e/detectors/test_data/cache-array-length/0.8.17/CacheArrayLength.sol#67) should use cached array length instead of referencing `length` member of the storage array. |
||||
|
@ -0,0 +1,24 @@ |
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#84 is incorrect - no matching function for bytes17[] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#85 is incorrect - no matching function for uint256 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#90 is incorrect - no matching function for mapping(int256 => uint128) found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#86 is incorrect - no matching function for int256 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#89 is incorrect - no matching function for E2 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#93 is incorrect - no matching function for bytes[][] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#92 is incorrect - no matching function for string[][] found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#91 is incorrect - no matching function for mapping(int128 => uint256) found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#87 is incorrect - no matching function for bytes18 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#88 is incorrect - no matching function for S2 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#83 is incorrect - no matching function for C3 found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
||||
using-for statement at tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#94 is incorrect - no matching function for custom_int found in L (tests/e2e/detectors/test_data/incorrect-using-for/0.8.17/IncorrectUsingForTopLevel.sol#48-64). |
||||
|
@ -1,5 +1,10 @@ |
||||
Contract locking ether found: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#26) has payable functions: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#37) has payable functions: |
||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
Contract locking ether found: |
||||
Contract UnlockedAssembly (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#27-35) has payable functions: |
||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.4.25/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
|
@ -1,5 +1,10 @@ |
||||
Contract locking ether found: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#26) has payable functions: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#37) has payable functions: |
||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
Contract locking ether found: |
||||
Contract UnlockedAssembly (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#27-35) has payable functions: |
||||
- Locked.receive() (tests/e2e/detectors/test_data/locked-ether/0.5.16/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
|
@ -1,5 +1,5 @@ |
||||
Contract locking ether found: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#26) has payable functions: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#36) has payable functions: |
||||
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.6.11/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
|
@ -1,5 +1,5 @@ |
||||
Contract locking ether found: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#26) has payable functions: |
||||
Contract OnlyLocked (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#36) has payable functions: |
||||
- Locked.receive_eth() (tests/e2e/detectors/test_data/locked-ether/0.7.6/locked_ether.sol#4-6) |
||||
But does not have a function to withdraw the ether |
||||
|
||||
|
@ -1,8 +1,8 @@ |
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#19)does not index parameter to |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#20)does not index parameter owner |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#20)does not index parameter owner |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#19)does not index parameter from |
||||
|
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#19)does not index parameter from |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#20)does not index parameter spender |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#20)does not index parameter spender |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.4.25/erc20_indexed.sol#19)does not index parameter to |
||||
|
||||
|
@ -1,8 +1,8 @@ |
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#19)does not index parameter to |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#20)does not index parameter owner |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#20)does not index parameter owner |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#19)does not index parameter from |
||||
|
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#19)does not index parameter from |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#20)does not index parameter spender |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#20)does not index parameter spender |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.5.16/erc20_indexed.sol#19)does not index parameter to |
||||
|
||||
|
@ -1,8 +1,8 @@ |
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#19)does not index parameter to |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#20)does not index parameter owner |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#20)does not index parameter owner |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#19)does not index parameter from |
||||
|
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#19)does not index parameter from |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#20)does not index parameter spender |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#20)does not index parameter spender |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.6.11/erc20_indexed.sol#19)does not index parameter to |
||||
|
||||
|
@ -1,8 +1,8 @@ |
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#19)does not index parameter to |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#20)does not index parameter owner |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#20)does not index parameter owner |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#19)does not index parameter from |
||||
|
||||
ERC20 event IERC20BadTransfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#19)does not index parameter from |
||||
ERC20 event IERC20Bad.Approval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#20)does not index parameter spender |
||||
|
||||
ERC20 event IERC20BadApproval(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#20)does not index parameter spender |
||||
ERC20 event IERC20Bad.Transfer(address,address,uint256) (tests/e2e/detectors/test_data/erc20-indexed/0.7.6/erc20_indexed.sol#19)does not index parameter to |
||||
|
||||
|
@ -1,2 +1,2 @@ |
||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol#4) is a local variable never initialized |
||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol#8) is a local variable never initialized |
||||
|
||||
|
@ -1,2 +1,2 @@ |
||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol#4) is a local variable never initialized |
||||
Uninitialized.func().uint_not_init (tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol#8) is a local variable never initialized |
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,171 @@ |
||||
pragma solidity 0.8.17; |
||||
|
||||
contract CacheArrayLength |
||||
{ |
||||
struct S |
||||
{ |
||||
uint s; |
||||
} |
||||
|
||||
S[] array; |
||||
S[] array2; |
||||
|
||||
function h() external |
||||
{ |
||||
|
||||
} |
||||
|
||||
function g() internal |
||||
{ |
||||
this.h(); |
||||
} |
||||
|
||||
function h_view() external view |
||||
{ |
||||
|
||||
} |
||||
|
||||
function g_view() internal view |
||||
{ |
||||
this.h_view(); |
||||
} |
||||
|
||||
function f() public |
||||
{ |
||||
// array accessed but length doesn't change |
||||
for (uint i = 0; i < array.length; i++) // warning should appear |
||||
{ |
||||
array[i] = S(0); |
||||
} |
||||
|
||||
// array.length doesn't change, but array.length not used in loop condition |
||||
for (uint i = array.length; i >= 0; i--) |
||||
{ |
||||
|
||||
} |
||||
|
||||
// array.length changes in the inner loop |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
for (uint j = i; j < 2 * i; j++) |
||||
array.push(S(j)); |
||||
} |
||||
|
||||
// array.length changes |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
array.pop(); |
||||
} |
||||
|
||||
// array.length changes |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
delete array; |
||||
} |
||||
|
||||
// array.length doesn't change despite using delete |
||||
for (uint i = 0; i < array.length; i++) // warning should appear |
||||
{ |
||||
delete array[i]; |
||||
} |
||||
|
||||
// array.length changes; push used in more complex expression |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
array.push() = S(i); |
||||
} |
||||
|
||||
// array.length doesn't change |
||||
for (uint i = 0; i < array.length; i++) // warning should appear |
||||
{ |
||||
array2.pop(); |
||||
array2.push(); |
||||
array2.push(S(i)); |
||||
delete array2; |
||||
delete array[0]; |
||||
} |
||||
|
||||
// array.length changes; array2.length doesn't change |
||||
for (uint i = 0; i < 7; i++) |
||||
{ |
||||
for (uint j = i; j < array.length; j++) |
||||
{ |
||||
for (uint k = 0; k < j; k++) |
||||
{ |
||||
|
||||
} |
||||
|
||||
for (uint k = 0; k < array2.length; k++) // warning should appear |
||||
{ |
||||
array.pop(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// array.length doesn't change; array2.length changes |
||||
for (uint i = 0; i < 7; i++) |
||||
{ |
||||
for (uint j = i; j < array.length; j++) // warning should appear |
||||
{ |
||||
for (uint k = 0; k < j; k++) |
||||
{ |
||||
|
||||
} |
||||
|
||||
for (uint k = 0; k < array2.length; k++) |
||||
{ |
||||
array2.pop(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// none of array.length and array2.length changes |
||||
for (uint i = 0; i < 7; i++) |
||||
{ |
||||
for (uint j = i; j < array.length; j++) // warning should appear |
||||
{ |
||||
for (uint k = 0; k < j; k++) |
||||
{ |
||||
|
||||
} |
||||
|
||||
for (uint k = 0; k < array2.length; k++) // warning should appear |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
S[] memory array3; |
||||
|
||||
// array3 not modified, but it's not a storage array |
||||
for (uint i = 0; i < array3.length; i++) |
||||
{ |
||||
|
||||
} |
||||
|
||||
// array not modified, but it may potentially change in an internal function call |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
g(); |
||||
} |
||||
|
||||
// array not modified, but it may potentially change in an external function call |
||||
for (uint i = 0; i < array.length; i++) |
||||
{ |
||||
this.h(); |
||||
} |
||||
|
||||
// array not modified and it cannot be changed in a function call since g_view is a view function |
||||
for (uint i = 0; i < array.length; i++) // warning should appear |
||||
{ |
||||
g_view(); |
||||
} |
||||
|
||||
// array not modified and it cannot be changed in a function call since h_view is a view function |
||||
for (uint i = 0; i < array.length; i++) // warning should appear |
||||
{ |
||||
this.h_view(); |
||||
} |
||||
} |
||||
} |
Binary file not shown.
@ -0,0 +1,94 @@ |
||||
pragma solidity 0.8.17; |
||||
|
||||
struct S1 |
||||
{ |
||||
uint __; |
||||
} |
||||
|
||||
struct S2 |
||||
{ |
||||
uint128 __; |
||||
} |
||||
|
||||
enum E1 |
||||
{ |
||||
A, |
||||
B |
||||
} |
||||
|
||||
enum E2 |
||||
{ |
||||
A, |
||||
B |
||||
} |
||||
|
||||
contract C0 |
||||
{ |
||||
|
||||
} |
||||
|
||||
contract C1 is C0 |
||||
{ |
||||
|
||||
} |
||||
|
||||
contract C2 is C1 |
||||
{ |
||||
|
||||
} |
||||
|
||||
contract C3 |
||||
{ |
||||
|
||||
} |
||||
|
||||
type custom_uint is uint248; |
||||
type custom_int is int248; |
||||
|
||||
library L |
||||
{ |
||||
function f0(C0) public pure {} |
||||
function f1(bool) public pure {} |
||||
function f2(string memory) public pure {} |
||||
function f3(bytes memory) public pure {} |
||||
function f4(uint248) public pure {} |
||||
function f5(int248) public pure {} |
||||
function f6(address) public pure {} |
||||
function f7(bytes17) public pure {} |
||||
function f8(S1 memory) public pure {} |
||||
function f9(E1) public pure {} |
||||
function f10(mapping(int => uint) storage) public pure {} |
||||
function f11(string[] memory) public pure {} |
||||
function f12(bytes[][][] memory) public pure {} |
||||
function f13(custom_uint) public pure {} |
||||
} |
||||
|
||||
// the following statements are correct |
||||
using L for C2; |
||||
using L for bool; |
||||
using L for string; |
||||
using L for bytes; |
||||
using L for uint240; |
||||
using L for int16; |
||||
using L for address; |
||||
using L for bytes16; |
||||
using L for S1; |
||||
using L for E1; |
||||
using L for mapping(int => uint); |
||||
using L for string[]; |
||||
using L for bytes[][][]; |
||||
using L for custom_uint; |
||||
|
||||
// the following statements are incorrect |
||||
using L for C3; |
||||
using L for bytes17[]; |
||||
using L for uint; |
||||
using L for int; |
||||
using L for bytes18; |
||||
using L for S2; |
||||
using L for E2; |
||||
using L for mapping(int => uint128); |
||||
using L for mapping(int128 => uint); |
||||
using L for string[][]; |
||||
using L for bytes[][]; |
||||
using L for custom_int; |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue