Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create return nodes for named implicit returns #1880

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
072cb0b
Find tainted functions/variables from external calls
webthethird Mar 29, 2023
c1e3743
Undo changes from new branch re: cross-contract taint
webthethird Mar 31, 2023
4b07282
Add failing test of `get_implementation_var`
webthethird Mar 31, 2023
11bbe69
Merge remote-tracking branch 'crytic/slither/dev' into dev-upgradeabi…
webthethird Mar 31, 2023
1e40d19
Add TODO
webthethird Mar 31, 2023
382c5b9
Create `RETURN` node for implicit (named) returns
webthethird May 2, 2023
c0764c9
Merge branch 'dev-upgradeability-utils' into dev-named-implicit-returns
webthethird May 2, 2023
ee5a790
Add `nodes` property getter to `YulBlock`
webthethird May 2, 2023
d7ef685
Fix implicit returns after parsing Yul blocks
webthethird May 2, 2023
d902457
Fix typo
webthethird May 2, 2023
80ac920
Black
webthethird May 2, 2023
457b80c
Handle legacy AST too
webthethird May 3, 2023
0a16995
Black
webthethird May 3, 2023
5e051ce
Merge branch 'dev' into dev-named-implicit-returns
webthethird May 15, 2023
91a63bb
Do not add implicit return after `THROW` node
webthethird May 15, 2023
ba3b604
Merge remote-tracking branch 'origin/dev-named-implicit-returns' into…
webthethird May 15, 2023
30794d0
Update expected json artifacts
webthethird May 15, 2023
53f4842
Merge branch 'dev' into dev-named-implicit-returns
webthethird May 16, 2023
643242a
Update expected json artifacts
webthethird May 16, 2023
d28bf61
Update compiles
webthethird May 16, 2023
f5ef00d
Update json artifacts
webthethird May 16, 2023
2d8fd3a
Add missing json artifact
webthethird May 16, 2023
d215d05
Bump
webthethird May 16, 2023
d2ae168
Add missing json artifact
webthethird May 16, 2023
3d8ff74
Merge remote-tracking branch 'origin/dev-named-implicit-returns' into…
webthethird May 16, 2023
3a0f005
Revert "Update expected json artifacts"
webthethird May 16, 2023
e3a8a98
Revert "Update compiles"
webthethird May 16, 2023
728e40c
Update 18 affected json artifacts
webthethird May 16, 2023
bffb172
Update expected json artifacts for conditional-all.sol
webthethird May 16, 2023
371ca7d
Add parser tests for implicit returns
webthethird May 16, 2023
e4864de
Fix expected json artifacts
webthethird May 17, 2023
0d93648
Remove debugging statement
webthethird May 17, 2023
326ec90
Remove AST parsing tests
webthethird May 18, 2023
ba1d536
Add legacy option to `slither_from_source` fixture
webthethird May 18, 2023
80c96cf
Begin test_implicit_returns.py
webthethird May 18, 2023
9c8eb75
Fix typos
webthethird May 18, 2023
e63b578
Fix typos, lint
webthethird May 18, 2023
242a04b
Remove implicit-returns.sol from test_ast_parsing.py
webthethird May 18, 2023
3cf394e
Add more test cases to test_implicit_returns.py
webthethird May 18, 2023
5875b54
Test legacy and compact in test_implicit_returns.py
webthethird May 18, 2023
d618971
Test additional tests in test_implicit_returns.py
webthethird May 18, 2023
e8a8081
Typo, solc version
webthethird May 18, 2023
b1d6177
Black
webthethird May 18, 2023
583d60a
Merge branch 'dev' into dev-named-implicit-returns
webthethird May 19, 2023
55da69e
Merge branch 'dev' into dev-named-implicit-returns
webthethird May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 141 additions & 3 deletions slither/solc_parsing/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def __init__(
else:
self._function.name = function_data["attributes"][self.get_key()]
self._functionNotParsed = function_data
self._returnsNotParsed: List[dict] = []
self._params_was_analyzed = False
self._content_was_analyzed = False

Expand Down Expand Up @@ -280,6 +281,7 @@ def analyze_content(self) -> None:

if self.is_compact_ast:
body = self._functionNotParsed.get("body", None)
return_ast = self._functionNotParsed.get("returnParameters", None)

if body and body[self.get_key()] == "Block":
self._function.is_implemented = True
Expand All @@ -290,6 +292,7 @@ def analyze_content(self) -> None:

else:
children = self._functionNotParsed[self.get_children("children")]
return_ast = children[1]
self._function.is_implemented = False
for child in children[2:]:
if child[self.get_key()] == "Block":
Expand All @@ -315,6 +318,9 @@ def analyze_content(self) -> None:

self._remove_alone_endif()

if return_ast:
self._fix_implicit_return(return_ast)

# endregion
###################################################################################
###################################################################################
Expand Down Expand Up @@ -1146,6 +1152,13 @@ def _fix_continue_node(self, node: Node) -> None:
node.set_sons([start_node])
start_node.add_father(node)

# endregion
###################################################################################
###################################################################################
# region Try-Catch
###################################################################################
###################################################################################

def _fix_try(self, node: Node) -> None:
end_node = next((son for son in node.sons if son.type != NodeType.CATCH), None)
if end_node:
Expand All @@ -1162,6 +1175,13 @@ def _fix_catch(self, node: Node, end_node: Node, visited: Set[Node]) -> None:
visited.add(son)
self._fix_catch(son, end_node, visited)

# endregion
###################################################################################
###################################################################################
# region Params, Returns, Modifiers
###################################################################################
###################################################################################

def _add_param(self, param: Dict) -> LocalVariableSolc:

local_var = LocalVariable()
Expand Down Expand Up @@ -1201,11 +1221,11 @@ def _parse_returns(self, returns: Dict):
self._function.returns_src().set_offset(returns["src"], self._function.compilation_unit)

if self.is_compact_ast:
returns = returns["parameters"]
self._returnsNotParsed = returns["parameters"]
else:
returns = returns[self.get_children("children")]
self._returnsNotParsed = returns[self.get_children("children")]

for ret in returns:
for ret in self._returnsNotParsed:
assert ret[self.get_key()] == "VariableDeclaration"
local_var = self._add_param(ret)
self._function.add_return(local_var.underlying_variable)
Expand Down Expand Up @@ -1259,6 +1279,124 @@ def _parse_modifier(self, modifier: Dict):
)
)

def _fix_implicit_return(self, cfg: dict) -> None:
if (
len(self.underlying_function.returns) == 0
or not any(ret.name != "" for ret in self.underlying_function.returns)
or not self._function.is_implemented
):
return
return_node = self._new_node(NodeType.RETURN, cfg["src"], self.underlying_function)
for node, node_solc in self._node_to_nodesolc.items():
if len(node.sons) == 0 and node.type not in [NodeType.RETURN, NodeType.THROW]:
link_underlying_nodes(node_solc, return_node)
for _, yul_block in self._node_to_yulobject.items():
for yul_node in yul_block.nodes:
node = yul_node.underlying_node
if len(node.sons) == 0 and node.type not in [NodeType.RETURN, NodeType.THROW]:
link_underlying_nodes(yul_node, return_node)
if self.is_compact_ast:
self._add_return_exp_compact(return_node, cfg)
else:
self._add_return_exp_legacy(return_node, cfg)
return_node.analyze_expressions(self)

def _add_return_exp_compact(self, return_node: NodeSolc, cfg: dict) -> None:
if len(self.underlying_function.returns) == 1:
return_arg = self.underlying_function.returns[0]
if return_arg.name != "":
(refId, refSrc, refType) = next(
(ret["id"], ret["src"], ret["typeDescriptions"])
for ret in self._returnsNotParsed
if ret["name"] == return_arg.name
)
return_node.add_unparsed_expression(
{
"name": return_arg.name,
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": refId,
"src": refSrc,
"typeDescriptions": refType,
}
)
else:
expression = {
"components": [],
"isConstant": False,
"isInlineArray": False,
"isLValue": False,
"isPure": False,
"lValueRequested": False,
"nodeType": "TupleExpression",
"src": cfg["src"],
"typeDescriptions": {},
}
type_ids = []
type_strs = []
for return_arg in self.underlying_function.returns:
if return_arg.name != "":
(refId, refSrc, refType) = next(
(ret["id"], ret["src"], ret["typeDescriptions"])
for ret in self._returnsNotParsed
if ret["name"] == return_arg.name
)
type_ids.append(refType["typeIdentifier"])
type_strs.append(refType["typeString"])
expression["components"].append(
{
"name": return_arg.name,
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": refId,
"src": refSrc,
"typeDescriptions": refType,
}
)
expression["typeDescriptions"]["typeIdentifier"] = (
"t_tuple$_" + "_$_".join(type_ids) + "_$"
)
expression["typeDescriptions"]["typeString"] = "tuple(" + ",".join(type_strs) + ")"
return_node.add_unparsed_expression(expression)

def _add_return_exp_legacy(self, return_node: NodeSolc, cfg: dict) -> None:
if len(self.underlying_function.returns) == 1:
return_arg = self.underlying_function.returns[0]
if return_arg.name != "":
(refSrc, refType) = next(
(ret["src"], ret["attributes"]["type"])
for ret in self._returnsNotParsed
if ret["attributes"]["name"] == return_arg.name
)
return_node.add_unparsed_expression(
{
"attributes": {"type": refType, "value": return_arg.name},
"name": "Identifier",
"src": refSrc,
}
)
else:
expression = {
"children": [],
"name": "TupleExpression",
"src": cfg["src"],
}
for return_arg in self.underlying_function.returns:
if return_arg.name != "":
(refSrc, refType) = next(
(ret["src"], ret["attributes"]["type"])
for ret in self._returnsNotParsed
if ret["attributes"]["name"] == return_arg.name
)
expression["children"].append(
{
"attributes": {"type": refType, "value": return_arg.name},
"name": "Identifier",
"src": refSrc,
}
)
return_node.add_unparsed_expression(expression)

# endregion
###################################################################################
###################################################################################
Expand Down
4 changes: 4 additions & 0 deletions slither/solc_parsing/yul/parse_yul.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ def entrypoint(self) -> YulNode:
def function(self) -> Function:
return self._parent_func

@property
def nodes(self) -> List[YulNode]:
return self._nodes

def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode:
if self._parent_func:
node = self._parent_func.new_node(node_type, src, self.node_scope)
Expand Down
26 changes: 20 additions & 6 deletions slither/utils/upgradeability.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Tuple, List, Union
from typing import Optional, Tuple, List, Union, TypedDict
from slither.core.declarations import (
Contract,
Structure,
Expand All @@ -19,10 +19,12 @@
from slither.core.variables.state_variable import StateVariable
from slither.analyses.data_dependency.data_dependency import get_dependencies
from slither.core.variables.variable import Variable
from slither.core.expressions.literal import Literal
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.call_expression import CallExpression
from slither.core.expressions.assignment_operation import AssignmentOperation
from slither.core.expressions import (
Literal,
Identifier,
CallExpression,
AssignmentOperation,
)
from slither.core.cfg.node import Node, NodeType
from slither.slithir.operations import (
Operation,
Expand Down Expand Up @@ -61,11 +63,22 @@
from slither.tools.read_storage.read_storage import SlotInfo, SlitherReadStorage


class TaintedExternalContract(TypedDict):
contract: Contract
functions: List[Function]
variables: List[Variable]


# pylint: disable=too-many-locals
def compare(
v1: Contract, v2: Contract
) -> Tuple[
List[Variable], List[Variable], List[Variable], List[Function], List[Function], List[Function]
List[Variable],
List[Variable],
List[Variable],
List[Function],
List[Function],
List[Function],
]:
"""
Compares two versions of a contract. Most useful for upgradeable (logic) contracts,
Expand Down Expand Up @@ -398,6 +411,7 @@ def get_proxy_implementation_var(proxy: Contract) -> Optional[Variable]:
try:
delegate = next(var for var in dependencies if isinstance(var, StateVariable))
except StopIteration:
# TODO: Handle case where get_dependencies does not return any state variables.
return delegate
return delegate

Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def inner(version):
@pytest.fixture
def slither_from_source(solc_binary_path):
@contextmanager
def inner(source_code: str, solc_version: str = "0.8.19"):
def inner(source_code: str, solc_version: str = "0.8.19", legacy: bool = False):
"""Yields a Slither instance using source_code string and solc_version.
Creates a temporary file and compiles with solc_version.
"""
Expand All @@ -72,7 +72,7 @@ def inner(source_code: str, solc_version: str = "0.8.19"):
fname = f.name
f.write(source_code)
solc_path = solc_binary_path(solc_version)
yield Slither(fname, solc=solc_path)
yield Slither(fname, solc=solc_path, force_legacy=legacy)
finally:
Path(fname).unlink()

Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/solc_parsing/test_ast_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ def _generate_compile(test_item: Test, skip_existing=False):
"To re-generate all the json artifacts run\n\tpython tests/test_ast_parsing.py --overwrite"
)
print("To compile json artifacts run\n\tpython tests/test_ast_parsing.py --compile")
print("\tThis will overwrite the previous json files")
print("\tThis will overwrite the previous json files.")
elif sys.argv[1] == "--generate":
for next_test in ALL_TESTS:
_generate_test(next_test, skip_existing=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"ContractArgCustomError": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"g()": "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: IF 2\n\"];\n2->3[label=\"True\"];\n2->4[label=\"False\"];\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: END_IF 4\n\"];\n}\n",
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n"
"h()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n"
}
}
Loading