#
tokens: 49304/50000 19/1140 files (page 10/103)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 10 of 103. Use http://codebase.md/cyfrin/aderyn?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .cargo
│   └── config.toml
├── .git-blame-ignore-revs
├── .gitattributes
├── .github
│   ├── images
│   │   ├── aderyn_logo.png
│   │   ├── poweredbycyfrinblack.png
│   │   └── poweredbycyfrinblue.png
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── false_positive_issue.md
│   │   └── feature_request.md
│   └── workflows
│       ├── cargo.yml
│       ├── dependencies.yml
│       ├── release.yml
│       ├── reports.yml
│       └── toml.yml
├── .gitignore
├── .gitmodules
├── .vscode
│   └── settings.json
├── aderyn
│   ├── Cargo.toml
│   ├── oranda.json
│   ├── README.md
│   ├── src
│   │   ├── birdsong.rs
│   │   ├── completions.rs
│   │   ├── lib.rs
│   │   ├── lsp.rs
│   │   ├── main.rs
│   │   ├── mcp.rs
│   │   └── panic.rs
│   └── templates
│       └── aderyn.toml
├── aderyn_core
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── README.md
│   ├── src
│   │   ├── ast
│   │   │   ├── ast_nodes.rs
│   │   │   ├── ast.rs
│   │   │   ├── impls
│   │   │   │   ├── ctx
│   │   │   │   │   ├── utils.rs
│   │   │   │   │   └── workspace.rs
│   │   │   │   ├── ctx.rs
│   │   │   │   ├── disp
│   │   │   │   │   ├── blocks.rs
│   │   │   │   │   ├── contracts.rs
│   │   │   │   │   ├── enumerations.rs
│   │   │   │   │   ├── errors.rs
│   │   │   │   │   ├── events.rs
│   │   │   │   │   ├── expressions.rs
│   │   │   │   │   ├── functions.rs
│   │   │   │   │   ├── identifiers.rs
│   │   │   │   │   ├── literals.rs
│   │   │   │   │   ├── modifiers.rs
│   │   │   │   │   ├── statements.rs
│   │   │   │   │   ├── structures.rs
│   │   │   │   │   ├── types.rs
│   │   │   │   │   ├── user_defined_value_types.rs
│   │   │   │   │   ├── using_for_directives.rs
│   │   │   │   │   └── variables.rs
│   │   │   │   ├── disp.rs
│   │   │   │   ├── node
│   │   │   │   │   ├── blocks.rs
│   │   │   │   │   ├── contracts.rs
│   │   │   │   │   ├── documentation.rs
│   │   │   │   │   ├── enumerations.rs
│   │   │   │   │   ├── errors.rs
│   │   │   │   │   ├── events.rs
│   │   │   │   │   ├── expressions.rs
│   │   │   │   │   ├── functions.rs
│   │   │   │   │   ├── identifiers.rs
│   │   │   │   │   ├── import_directives.rs
│   │   │   │   │   ├── literals.rs
│   │   │   │   │   ├── modifiers.rs
│   │   │   │   │   ├── pragma_directives.rs
│   │   │   │   │   ├── source_units.rs
│   │   │   │   │   ├── statements.rs
│   │   │   │   │   ├── structures.rs
│   │   │   │   │   ├── types.rs
│   │   │   │   │   ├── user_defined_value_types.rs
│   │   │   │   │   ├── using_for_directives.rs
│   │   │   │   │   └── variables.rs
│   │   │   │   ├── node.rs
│   │   │   │   ├── own
│   │   │   │   │   ├── hashing.rs
│   │   │   │   │   ├── node_id.rs
│   │   │   │   │   ├── source_units.rs
│   │   │   │   │   └── utils.rs
│   │   │   │   └── own.rs
│   │   │   ├── impls.rs
│   │   │   ├── macros.rs
│   │   │   ├── magic.rs
│   │   │   ├── node_type.rs
│   │   │   └── yul.rs
│   │   ├── ast.rs
│   │   ├── audit
│   │   │   ├── attack_surface.rs
│   │   │   ├── auditor.rs
│   │   │   ├── entrypoint.rs
│   │   │   └── public_functions_no_sender.rs
│   │   ├── audit.rs
│   │   ├── context
│   │   │   ├── browser
│   │   │   │   ├── ancestral_line.rs
│   │   │   │   ├── closest_ancestor.rs
│   │   │   │   ├── external_calls.rs
│   │   │   │   ├── extractor.rs
│   │   │   │   ├── immediate_children.rs
│   │   │   │   ├── location.rs
│   │   │   │   ├── macros.rs
│   │   │   │   ├── parent.rs
│   │   │   │   ├── peek_over.rs
│   │   │   │   ├── peek_under.rs
│   │   │   │   ├── peek.rs
│   │   │   │   ├── siblings.rs
│   │   │   │   ├── sort_nodes.rs
│   │   │   │   └── storage_vars.rs
│   │   │   ├── browser.rs
│   │   │   ├── capturable.rs
│   │   │   ├── flow
│   │   │   │   ├── display.rs
│   │   │   │   ├── error.rs
│   │   │   │   ├── kind.rs
│   │   │   │   ├── primitives.rs
│   │   │   │   ├── reducibles.rs
│   │   │   │   ├── tests.rs
│   │   │   │   ├── utils.rs
│   │   │   │   ├── visualizer.rs
│   │   │   │   └── voids.rs
│   │   │   ├── flow.rs
│   │   │   ├── graph
│   │   │   │   ├── callgraph
│   │   │   │   │   ├── legacy.rs
│   │   │   │   │   ├── new.rs
│   │   │   │   │   ├── tests.rs
│   │   │   │   │   ├── utils.rs
│   │   │   │   │   └── visit.rs
│   │   │   │   ├── callgraph.rs
│   │   │   │   ├── preprocess
│   │   │   │   │   ├── legacy.rs
│   │   │   │   │   └── new.rs
│   │   │   │   ├── preprocess.rs
│   │   │   │   ├── traits.rs
│   │   │   │   └── utils.rs
│   │   │   ├── graph.rs
│   │   │   ├── macros.rs
│   │   │   ├── mcp
│   │   │   │   ├── callgraph
│   │   │   │   │   ├── render.rs
│   │   │   │   │   ├── tool.rs
│   │   │   │   │   └── utils.rs
│   │   │   │   ├── callgraph.rs
│   │   │   │   ├── contract_surface
│   │   │   │   │   ├── render.rs
│   │   │   │   │   ├── tool.rs
│   │   │   │   │   └── util.rs
│   │   │   │   ├── contract_surface.rs
│   │   │   │   ├── list_contracts
│   │   │   │   │   ├── render.rs
│   │   │   │   │   └── tool.rs
│   │   │   │   ├── list_contracts.rs
│   │   │   │   ├── node_finder
│   │   │   │   │   ├── render.rs
│   │   │   │   │   ├── tool.rs
│   │   │   │   │   └── utils.rs
│   │   │   │   ├── node_finder.rs
│   │   │   │   ├── node_summarizer
│   │   │   │   │   ├── render.rs
│   │   │   │   │   ├── tool.rs
│   │   │   │   │   └── utils.rs
│   │   │   │   ├── node_summarizer.rs
│   │   │   │   ├── project_overview
│   │   │   │   │   ├── render.rs
│   │   │   │   │   └── tool.rs
│   │   │   │   ├── project_overview.rs
│   │   │   │   ├── tool_guide
│   │   │   │   │   └── tool.rs
│   │   │   │   └── tool_guide.rs
│   │   │   ├── mcp.rs
│   │   │   ├── router
│   │   │   │   ├── external_calls.rs
│   │   │   │   ├── internal_calls.rs
│   │   │   │   ├── modifier_calls.rs
│   │   │   │   └── tests.rs
│   │   │   ├── router.rs
│   │   │   └── workspace.rs
│   │   ├── context.rs
│   │   ├── detect
│   │   │   ├── detector.rs
│   │   │   ├── entrypoint.rs
│   │   │   ├── helpers.rs
│   │   │   ├── high
│   │   │   │   ├── _template.rs
│   │   │   │   ├── abi_encode_packed_hash_collision.rs
│   │   │   │   ├── arbitrary_transfer_from.rs
│   │   │   │   ├── const_func_changes_state.rs
│   │   │   │   ├── contract_locks_ether.rs
│   │   │   │   ├── dangerous_unary_operator.rs
│   │   │   │   ├── delegate_call_unchecked_address.rs
│   │   │   │   ├── delete_nested_mapping.rs
│   │   │   │   ├── dynamic_array_length_assignment.rs
│   │   │   │   ├── enumerable_loop_removal.rs
│   │   │   │   ├── eth_send_unchecked_address.rs
│   │   │   │   ├── experimental_encoder.rs
│   │   │   │   ├── function_selector_collision.rs
│   │   │   │   ├── incorrect_caret_operator.rs
│   │   │   │   ├── incorrect_erc20_interface.rs
│   │   │   │   ├── incorrect_erc721_interface.rs
│   │   │   │   ├── incorrect_shift_order.rs
│   │   │   │   ├── misused_boolean.rs
│   │   │   │   ├── msg_value_in_loops.rs
│   │   │   │   ├── multiple_constructors.rs
│   │   │   │   ├── nested_struct_in_mapping.rs
│   │   │   │   ├── out_of_order_retryable.rs
│   │   │   │   ├── pre_declared_variable_usage.rs
│   │   │   │   ├── reentrancy_state_change.rs
│   │   │   │   ├── reused_contract_name.rs
│   │   │   │   ├── rtlo.rs
│   │   │   │   ├── selfdestruct.rs
│   │   │   │   ├── signed_integer_storage_array.rs
│   │   │   │   ├── state_variable_shadowing.rs
│   │   │   │   ├── storage_array_memory_edit.rs
│   │   │   │   ├── strict_equality_contract_balance.rs
│   │   │   │   ├── tautological_compare.rs
│   │   │   │   ├── tautology_or_contradiction.rs
│   │   │   │   ├── tx_origin_used_for_auth.rs
│   │   │   │   ├── unchecked_low_level_call.rs
│   │   │   │   ├── unchecked_send.rs
│   │   │   │   ├── unprotected_initializer.rs
│   │   │   │   ├── unsafe_casting.rs
│   │   │   │   ├── weak_randomness.rs
│   │   │   │   └── yul_return.rs
│   │   │   ├── high.rs
│   │   │   ├── low
│   │   │   │   ├── _template.rs
│   │   │   │   ├── assert_state_change.rs
│   │   │   │   ├── block_timestamp_deadline.rs
│   │   │   │   ├── boolean_equality.rs
│   │   │   │   ├── builtin_symbol_shadowing.rs
│   │   │   │   ├── centralization_risk.rs
│   │   │   │   ├── constant_function_contains_assembly.rs
│   │   │   │   ├── costly_loop.rs
│   │   │   │   ├── dead_code.rs
│   │   │   │   ├── delegatecall_in_loop.rs
│   │   │   │   ├── deprecated_oz_function.rs
│   │   │   │   ├── division_before_multiplication.rs
│   │   │   │   ├── ecrecover.rs
│   │   │   │   ├── empty_block.rs
│   │   │   │   ├── empty_require_revert.rs
│   │   │   │   ├── function_initializing_state.rs
│   │   │   │   ├── function_pointer_in_constructor.rs
│   │   │   │   ├── inconsistent_type_names.rs
│   │   │   │   ├── incorrect_modifier.rs
│   │   │   │   ├── internal_function_used_once.rs
│   │   │   │   ├── large_numeric_literal.rs
│   │   │   │   ├── literal_instead_of_constant.rs
│   │   │   │   ├── local_variable_shadowing.rs
│   │   │   │   ├── missing_inheritance.rs
│   │   │   │   ├── modifier_used_only_once.rs
│   │   │   │   ├── multiple_placeholders.rs
│   │   │   │   ├── non_reentrant_not_first.rs
│   │   │   │   ├── push_0_opcode.rs
│   │   │   │   ├── redundant_statement.rs
│   │   │   │   ├── require_revert_in_loop.rs
│   │   │   │   ├── return_bomb.rs
│   │   │   │   ├── solmate_safe_transfer_lib.rs
│   │   │   │   ├── state_change_without_event.rs
│   │   │   │   ├── state_no_address_check.rs
│   │   │   │   ├── state_variable_could_be_constant.rs
│   │   │   │   ├── state_variable_could_be_immutable.rs
│   │   │   │   ├── state_variable_read_external.rs
│   │   │   │   ├── storage_array_length_not_cached.rs
│   │   │   │   ├── todo.rs
│   │   │   │   ├── unchecked_return.rs
│   │   │   │   ├── uninitialized_local_variable.rs
│   │   │   │   ├── unsafe_erc20_operation.rs
│   │   │   │   ├── unsafe_oz_erc721_mint.rs
│   │   │   │   ├── unspecific_solidity_pragma.rs
│   │   │   │   ├── unused_error.rs
│   │   │   │   ├── unused_import.rs
│   │   │   │   ├── unused_public_function.rs
│   │   │   │   ├── unused_state_variable.rs
│   │   │   │   └── void_constructor.rs
│   │   │   ├── low.rs
│   │   │   └── test_utils.rs
│   │   ├── detect.rs
│   │   ├── lib.rs
│   │   ├── stats
│   │   │   ├── cloc.rs
│   │   │   ├── dbg_tips.txt
│   │   │   ├── ignore.rs
│   │   │   ├── token.rs
│   │   │   └── util.rs
│   │   ├── stats.rs
│   │   ├── test_utils
│   │   │   └── load_source_unit.rs
│   │   ├── test_utils.rs
│   │   ├── visitor
│   │   │   ├── ast_visitor.rs
│   │   │   ├── macros.rs
│   │   │   └── workspace_visitor.rs
│   │   └── visitor.rs
│   ├── templates
│   │   └── mcp-tool-response
│   │       ├── callgraph.md
│   │       ├── contract_surface.md
│   │       ├── list_contracts.md
│   │       ├── node_finder_get_all.md
│   │       ├── node_finder_grep.md
│   │       ├── node_finder_search.md
│   │       ├── node_summarizer.md
│   │       ├── project_overview.md
│   │       └── tool_guide.md
│   └── tests
│       ├── common
│       │   ├── ancestral_line.rs
│       │   ├── closest_ancestor.rs
│       │   ├── immediate_children.rs
│       │   ├── immediate_parent.rs
│       │   ├── mod.rs
│       │   ├── new_ast_nodes.rs
│       │   ├── peek_over.rs
│       │   └── sibling.rs
│       └── traversal.rs
├── aderyn_driver
│   ├── .gitignore
│   ├── benches
│   │   └── detectors.rs
│   ├── Cargo.toml
│   ├── README.md
│   ├── src
│   │   ├── compile.rs
│   │   ├── config.rs
│   │   ├── display.rs
│   │   ├── driver.rs
│   │   ├── interface
│   │   │   ├── json.rs
│   │   │   ├── lsp.rs
│   │   │   ├── markdown.rs
│   │   │   ├── mod.rs
│   │   │   ├── sarif.rs
│   │   │   ├── tables.rs
│   │   │   └── util.rs
│   │   ├── lib.rs
│   │   ├── mcp.rs
│   │   ├── process.rs
│   │   └── runner.rs
│   └── tests
│       └── astgen.rs
├── bacon.toml
├── benchmarks
│   ├── aderyn
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── iteration_times.svg
│   │       │   └── pdf.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── iteration_times_small.svg
│   │       ├── iteration_times.svg
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── relative_iteration_times_small.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── SD.svg
│   │       └── typical.svg
│   ├── arbitrary-transfer-from
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── avoid-abi-encode-packed
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── block-timestamp-deadline
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── centralization-risk
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── constants-instead-of-literals
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── delegate-call-in-loop
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── deprecated-oz-functions
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── ecrecover
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── empty-block
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── hello_world
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── inconsistent-type-names
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── large-numeric-literal
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── non-reentrant-before-others
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── push-zero-opcode
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── report
│   │   └── index.html
│   ├── require-with-string
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── solmate-safe-transfer-lib
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── unindexed-events
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── unprotected-initializer
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── unsafe-erc20-functions
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── unsafe-oz-erc721-mint
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── unspecific-solidity-pragma
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── useless-internal-function
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── useless-modifier
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   ├── useless-public-function
│   │   ├── base
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   ├── change
│   │   │   └── estimates.json
│   │   ├── new
│   │   │   ├── benchmark.json
│   │   │   ├── estimates.json
│   │   │   ├── sample.json
│   │   │   └── tukey.json
│   │   └── report
│   │       ├── both
│   │       │   ├── pdf.svg
│   │       │   └── regression.svg
│   │       ├── change
│   │       │   ├── mean.svg
│   │       │   ├── median.svg
│   │       │   └── t-test.svg
│   │       ├── index.html
│   │       ├── MAD.svg
│   │       ├── mean.svg
│   │       ├── median.svg
│   │       ├── pdf_small.svg
│   │       ├── pdf.svg
│   │       ├── regression_small.svg
│   │       ├── regression.svg
│   │       ├── relative_pdf_small.svg
│   │       ├── relative_regression_small.svg
│   │       ├── SD.svg
│   │       ├── slope.svg
│   │       └── typical.svg
│   └── zero-address-check
│       ├── base
│       │   ├── benchmark.json
│       │   ├── estimates.json
│       │   ├── sample.json
│       │   └── tukey.json
│       ├── change
│       │   └── estimates.json
│       ├── new
│       │   ├── benchmark.json
│       │   ├── estimates.json
│       │   ├── sample.json
│       │   └── tukey.json
│       └── report
│           ├── both
│           │   ├── pdf.svg
│           │   └── regression.svg
│           ├── change
│           │   ├── mean.svg
│           │   ├── median.svg
│           │   └── t-test.svg
│           ├── index.html
│           ├── MAD.svg
│           ├── mean.svg
│           ├── median.svg
│           ├── pdf_small.svg
│           ├── pdf.svg
│           ├── regression_small.svg
│           ├── regression.svg
│           ├── relative_pdf_small.svg
│           ├── relative_regression_small.svg
│           ├── SD.svg
│           ├── slope.svg
│           └── typical.svg
├── Cargo.lock
├── Cargo.toml
├── cli
│   ├── benchmarks.sh
│   └── reportgen.sh
├── CODEOWNERS
├── CONTRIBUTING.md
├── cyfrinup
│   ├── dynamic_script
│   └── why.md
├── deny.toml
├── dist-workspace.toml
├── funding.json
├── LICENSE
├── Makefile
├── package-lock.json
├── package.json
├── README.md
├── RELEASE_CHECKLIST.md
├── reports
│   ├── adhoc-sol-files-highs-only-report.json
│   ├── adhoc-sol-files-report.md
│   ├── ccip-functions-report.md
│   ├── empty_report.md
│   ├── hardhat-playground-report.md
│   ├── nft-report-icm.md
│   ├── nft-report.md
│   ├── prb-math-report.md
│   ├── report.json
│   ├── report.md
│   ├── report.sarif
│   ├── sablier-aderyn-toml-nested-root.md
│   ├── templegold-report.md
│   └── uniswap_profile.md
├── rust-toolchain.toml
├── rustfmt.toml
├── tests
│   ├── adhoc-sol-files
│   │   ├── aderyn.toml
│   │   ├── Counter.sol
│   │   ├── DemoASTNodes.sol
│   │   ├── Helper.sol
│   │   ├── InconsistentUints.sol
│   │   ├── inheritance
│   │   │   ├── ExtendedInheritance.sol
│   │   │   ├── IContractInheritance.sol
│   │   │   └── InheritanceBase.sol
│   │   ├── InternalFunctions.sol
│   │   ├── lib
│   │   │   └── ThisShouldBeExcluded.sol
│   │   ├── multiple-versions
│   │   │   ├── 0.4
│   │   │   │   ├── A.sol
│   │   │   │   └── B.sol
│   │   │   ├── 0.5
│   │   │   │   ├── A.sol
│   │   │   │   └── B.sol
│   │   │   ├── 0.6
│   │   │   │   ├── A.sol
│   │   │   │   └── B.sol
│   │   │   ├── 0.7
│   │   │   │   ├── A.sol
│   │   │   │   └── B.sol
│   │   │   └── 0.8
│   │   │       ├── A.sol
│   │   │       └── B.sol
│   │   ├── OnceModifierExample.sol
│   │   └── StateVariables.sol
│   ├── ast
│   │   ├── abstract_contract.json
│   │   ├── address_payable.json
│   │   ├── array_type_name.json
│   │   ├── ast-erc4626.json
│   │   ├── base_constructor_call.json
│   │   ├── bit_not.json
│   │   ├── call.json
│   │   ├── constructor.json
│   │   ├── contract_dep_order.json
│   │   ├── do_while.json
│   │   ├── documentation_1.json
│   │   ├── documentation_2.json
│   │   ├── documentation_3.json
│   │   ├── documentation_local_variable.json
│   │   ├── documentation_on_statements.json
│   │   ├── documentation_triple.json
│   │   ├── empty_block.json
│   │   ├── enum_value_declaration.json
│   │   ├── enum_value.json
│   │   ├── event_definition.json
│   │   ├── experimental_encoder_pragma.json
│   │   ├── fallback_and_reveice_ether.json
│   │   ├── fallback_payable.json
│   │   ├── fallback.json
│   │   ├── function_type.json
│   │   ├── function.json
│   │   ├── global_enum.json
│   │   ├── global_struct.json
│   │   ├── inheritance_specifier.json
│   │   ├── leave.json
│   │   ├── license.json
│   │   ├── long_type_name_binary_operation.json
│   │   ├── long_type_name_identifier.json
│   │   ├── loop.json
│   │   ├── mappings.json
│   │   ├── modifier_definition.json
│   │   ├── modifier_invocation.json
│   │   ├── mutability.json
│   │   ├── nested_functions.json
│   │   ├── non_utf8.json
│   │   ├── override.json
│   │   ├── placeholder_statement.json
│   │   ├── receive_ether.json
│   │   ├── short_type_name_ref.json
│   │   ├── short_type_name.json
│   │   ├── slot_offset.json
│   │   ├── smoke.json
│   │   ├── source_location.json
│   │   ├── string.json
│   │   ├── stringlit.json
│   │   ├── switch_default.json
│   │   ├── switch.json
│   │   ├── try_catch.json
│   │   ├── two_base_functions.json
│   │   ├── unicode.json
│   │   ├── used_errors.json
│   │   ├── userDefinedValueType.json
│   │   ├── using_for_directive.json
│   │   ├── var_access.json
│   │   └── yul_hex_literal.json
│   ├── contract-playground
│   │   ├── .github
│   │   │   └── workflows
│   │   │       └── test.yml
│   │   ├── .gitignore
│   │   ├── dot
│   │   │   └── .gitkeep
│   │   ├── foundry.toml
│   │   ├── README.md
│   │   ├── script
│   │   │   └── Counter.s.sol
│   │   ├── src
│   │   │   ├── AbstractContract.sol
│   │   │   ├── AderynIgnoreCustomDetectors.sol
│   │   │   ├── AdminContract.sol
│   │   │   ├── ArbitraryTransferFrom.sol
│   │   │   ├── AssemblyExample.sol
│   │   │   ├── AssertStateChange.sol
│   │   │   ├── auditor_mode
│   │   │   │   ├── ExternalCalls.sol
│   │   │   │   └── PublicFunctionsWithoutSenderCheck.sol
│   │   │   ├── BooleanEquality.sol
│   │   │   ├── BuiltinSymbolShadow.sol
│   │   │   ├── CacheArrayLength.sol
│   │   │   ├── CallGraphTests.sol
│   │   │   ├── Casting.sol
│   │   │   ├── cloc
│   │   │   │   ├── AnotherHeavilyCommentedContract.sol
│   │   │   │   ├── EmptyContractFile.sol
│   │   │   │   └── HeavilyCommentedContract.sol
│   │   │   ├── CompilerBugStorageSignedIntegerArray.sol
│   │   │   ├── ConstantFuncsAssembly.sol
│   │   │   ├── ConstantsLiterals.sol
│   │   │   ├── ConstFuncChangeState.sol
│   │   │   ├── ContractLocksEther.sol
│   │   │   ├── ContractWithTodo.sol
│   │   │   ├── control_flow
│   │   │   │   └── SimpleProgram.sol
│   │   │   ├── CostlyOperationsInsideLoops.sol
│   │   │   ├── Counter.sol
│   │   │   ├── CrazyPragma.sol
│   │   │   ├── DangerousStrictEquality1.sol
│   │   │   ├── DangerousStrictEquality2.sol
│   │   │   ├── DangerousUnaryOperator.sol
│   │   │   ├── DeadCode.sol
│   │   │   ├── DelegateCallWithoutAddressCheck.sol
│   │   │   ├── DeletionNestedMappingStructureContract.sol
│   │   │   ├── DeprecatedOZFunctions.sol
│   │   │   ├── DivisionBeforeMultiplication.sol
│   │   │   ├── DynamicArrayLengthAssignment.sol
│   │   │   ├── EmitAfterExternalCall.sol
│   │   │   ├── EmptyBlocks.sol
│   │   │   ├── EnumerableSetIteration.sol
│   │   │   ├── eth2
│   │   │   │   └── DepositContract.sol
│   │   │   ├── ExperimentalEncoder.sol
│   │   │   ├── ExternalCalls.sol
│   │   │   ├── FunctionInitializingState.sol
│   │   │   ├── FunctionPointers.sol
│   │   │   ├── FunctionSignatureCollision.sol
│   │   │   ├── HugeConstants.sol
│   │   │   ├── IgnoreEverything.sol
│   │   │   ├── InconsistentUints.sol
│   │   │   ├── IncorrectCaretOperator.sol
│   │   │   ├── IncorrectERC20.sol
│   │   │   ├── IncorrectERC721.sol
│   │   │   ├── IncorrectModifier.sol
│   │   │   ├── IncorrectShift.sol
│   │   │   ├── inheritance
│   │   │   │   ├── ExtendedInheritance.sol
│   │   │   │   ├── IContractInheritance.sol
│   │   │   │   └── InheritanceBase.sol
│   │   │   ├── InternalFunctions.sol
│   │   │   ├── KeccakContract.sol
│   │   │   ├── LocalVariableShadow.sol
│   │   │   ├── MissingInheritance.sol
│   │   │   ├── MisusedBoolean.sol
│   │   │   ├── MsgValueInLoop.sol
│   │   │   ├── MultipleConstructorSchemes.sol
│   │   │   ├── MultiplePlaceholders.sol
│   │   │   ├── nested
│   │   │   │   ├── 1
│   │   │   │   │   └── Nested.sol
│   │   │   │   └── 2
│   │   │   │       └── Nested.sol
│   │   │   ├── nested_mappings
│   │   │   │   ├── LaterVersion.sol
│   │   │   │   └── NestedMappings.sol
│   │   │   ├── OnceModifierExample.sol
│   │   │   ├── OnlyLibrary.sol
│   │   │   ├── OutOfOrderRetryable.sol
│   │   │   ├── parent_chain
│   │   │   │   └── ParentChainContract.sol
│   │   │   ├── PragmaRange.sol
│   │   │   ├── PreDeclaredVarUsage.sol
│   │   │   ├── PublicFunction.sol
│   │   │   ├── PublicVariableReadInExternalContext.sol
│   │   │   ├── RedundantStatements.sol
│   │   │   ├── ReturnBomb.sol
│   │   │   ├── reused_contract_name
│   │   │   │   ├── ContractA.sol
│   │   │   │   └── ContractB.sol
│   │   │   ├── RevertsAndRequriesInLoops.sol
│   │   │   ├── router
│   │   │   │   ├── ExternalCalls.sol
│   │   │   │   ├── FallbackAndReceiveOverrides.sol
│   │   │   │   ├── InternalCalls.sol
│   │   │   │   ├── ModifierCalls.sol
│   │   │   │   └── VarOverridesFunction.sol
│   │   │   ├── RTLO.sol
│   │   │   ├── SendEtherNoChecks.sol
│   │   │   ├── SendEtherNoChecksLibImport.sol
│   │   │   ├── StateChangeAfterExternalCall.sol
│   │   │   ├── StateShadowing.sol
│   │   │   ├── StateVariableCouldBeDeclaredConstant.sol
│   │   │   ├── StateVariableCouldBeDeclaredImmutable.sol
│   │   │   ├── StateVariables.sol
│   │   │   ├── StateVariablesChangesWithoutEvents.sol
│   │   │   ├── StateVariablesManipulation.sol
│   │   │   ├── StorageConditionals.sol
│   │   │   ├── StorageParameters.sol
│   │   │   ├── T11sTranferer.sol
│   │   │   ├── TautologicalCompare.sol
│   │   │   ├── TautologyOrContradiction.sol
│   │   │   ├── TestERC20.sol
│   │   │   ├── TransientKeyword.sol
│   │   │   ├── Trump.sol
│   │   │   ├── TxOriginUsedForAuth.sol
│   │   │   ├── U2.sol
│   │   │   ├── U3.sol
│   │   │   ├── U4.sol
│   │   │   ├── U5.sol
│   │   │   ├── UncheckedCalls.sol
│   │   │   ├── UncheckedReturn.sol
│   │   │   ├── UncheckedSend.sol
│   │   │   ├── UninitializedLocalVariables.sol
│   │   │   ├── UninitializedStateVariable.sol
│   │   │   ├── uniswap
│   │   │   │   ├── UniswapV2Swapper.sol
│   │   │   │   └── UniswapV3Swapper.sol
│   │   │   ├── UnprotectedInitialize.sol
│   │   │   ├── UnsafeERC721Mint.sol
│   │   │   ├── UnusedError.sol
│   │   │   ├── UnusedImport.sol
│   │   │   ├── UnusedStateVariables.sol
│   │   │   ├── UsingSelfdestruct.sol
│   │   │   ├── VoidConstructor.sol
│   │   │   ├── WeakRandomness.sol
│   │   │   ├── WrongOrderOfLayout.sol
│   │   │   ├── YulReturn.sol
│   │   │   └── ZeroAddressCheck.sol
│   │   └── test
│   │       └── Counter.t.sol
│   ├── foundry-nft-f23
│   │   ├── .github
│   │   │   └── workflows
│   │   │       └── test.yml
│   │   ├── .gitignore
│   │   ├── foundry.lock
│   │   ├── foundry.toml
│   │   ├── README.md
│   │   ├── remappings.txt
│   │   └── src
│   │       ├── BasicNft.sol
│   │       ├── F1.sol
│   │       ├── F2.sol
│   │       ├── Initializer.sol
│   │       └── inner-core-modules
│   │           └── ICM.sol
│   ├── foundry-nft-f23-icm
│   │   ├── .github
│   │   │   └── workflows
│   │   │       └── test.yml
│   │   ├── .gitignore
│   │   ├── aderyn.toml
│   │   ├── foundry.toml
│   │   ├── README.md
│   │   ├── remappings.txt
│   │   └── src
│   │       ├── BasicNft.sol
│   │       ├── F1.sol
│   │       ├── F2.sol
│   │       ├── Initializer.sol
│   │       └── inner-core-modules
│   │           └── ICM.sol
│   ├── hardhat-js-playground
│   │   ├── .gitignore
│   │   ├── artifacts
│   │   │   ├── build-info
│   │   │   │   └── cee6fe9a9a2f03f7ff10a27ab2746af6.json
│   │   │   └── contracts
│   │   │       ├── Counter.sol
│   │   │       │   ├── Counter.dbg.json
│   │   │       │   └── Counter.json
│   │   │       ├── ExtendedInheritance.sol
│   │   │       │   ├── ExtendedInheritance.dbg.json
│   │   │       │   └── ExtendedInheritance.json
│   │   │       ├── IContractInheritance.sol
│   │   │       │   ├── IContractInheritance.dbg.json
│   │   │       │   └── IContractInheritance.json
│   │   │       ├── InheritanceBase.sol
│   │   │       │   ├── InheritanceBase.dbg.json
│   │   │       │   └── InheritanceBase.json
│   │   │       ├── KeccakContract.sol
│   │   │       │   ├── KeccakContract.dbg.json
│   │   │       │   └── KeccakContract.json
│   │   │       ├── Lock.sol
│   │   │       │   ├── Lock.dbg.json
│   │   │       │   └── Lock.json
│   │   │       └── StateVariables.sol
│   │   │           ├── StateVariables.dbg.json
│   │   │           └── StateVariables.json
│   │   ├── contracts
│   │   │   ├── Counter.sol
│   │   │   ├── ExtendedInheritance.sol
│   │   │   ├── IContractInheritance.sol
│   │   │   ├── InheritanceBase.sol
│   │   │   ├── KeccakContract.sol
│   │   │   ├── Lock.sol
│   │   │   └── StateVariables.sol
│   │   ├── hardhat.config.js
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── scripts
│   │   │   └── deploy.js
│   │   ├── test
│   │   │   └── Lock.js
│   │   └── yarn.lock
│   ├── no-sol-files
│   │   ├── extra
│   │   │   └── HelloAgain.md
│   │   ├── Hello.txt
│   │   └── Hello.yul
│   └── toml
│       ├── nested_project1
│       │   ├── aderyn.toml
│       │   ├── folder1
│       │   │   └── hardhat.config.ts
│       │   ├── folder2
│       │   │   └── hardhat.config.ts
│       │   └── folder3
│       │       └── file.txt
│       └── nested_project2
│           ├── aderyn.toml
│           ├── folder1
│           │   └── foundry.toml
│           └── folder2
│               └── file1.txt
├── tools
│   └── xtask
│       ├── Cargo.toml
│       └── src
│           ├── blesspr.rs
│           ├── cut_release.rs
│           ├── flags.rs
│           ├── main.rs
│           ├── reportgen.rs
│           └── tomlgen.rs
└── typos.toml
```

# Files

--------------------------------------------------------------------------------
/aderyn_core/src/ast/impls/ctx/utils.rs:
--------------------------------------------------------------------------------

```rust
  1 | use crate::{ast::*, context::workspace::WorkspaceContext};
  2 | 
  3 | impl ContractDefinition {
  4 |     /// Returns sequence of all inherited contracts including itself in C3 linearized hierarchy
  5 |     #[inline]
  6 |     pub fn c3<'a>(
  7 |         &'a self,
  8 |         context: &'a WorkspaceContext,
  9 |     ) -> impl Iterator<Item = &'a ContractDefinition> {
 10 |         self.linearized_base_contracts.iter().flat_map(|c_id| context.nodes.get(c_id)).flat_map(
 11 |             |n| {
 12 |                 if let ASTNode::ContractDefinition(c) = n {
 13 |                     return Some(c);
 14 |                 }
 15 |                 None
 16 |             },
 17 |         )
 18 |     }
 19 | 
 20 |     #[inline]
 21 |     pub fn next_in<'a>(
 22 |         &'a self,
 23 |         context: &'a WorkspaceContext,
 24 |         base_contract: &'a ContractDefinition,
 25 |     ) -> Option<&'a ContractDefinition> {
 26 |         let mut base_c3 = base_contract.c3(context);
 27 |         while let Some(c) = base_c3.next() {
 28 |             if c.id == self.id {
 29 |                 return base_c3.next();
 30 |             }
 31 |         }
 32 |         None
 33 |     }
 34 | 
 35 |     #[inline]
 36 |     pub fn is_in<'a>(
 37 |         &'a self,
 38 |         context: &'a WorkspaceContext,
 39 |         base_contract: &'a ContractDefinition,
 40 |     ) -> bool {
 41 |         let base_c3 = base_contract.c3(context);
 42 |         for c in base_c3 {
 43 |             if c.id == self.id {
 44 |                 return true;
 45 |             }
 46 |         }
 47 |         false
 48 |     }
 49 | 
 50 |     pub fn entrypoint_functions<'a>(
 51 |         &'a self,
 52 |         context: &'a WorkspaceContext,
 53 |     ) -> Option<Vec<&'a FunctionDefinition>> {
 54 |         context.entrypoint_functions(self)
 55 |     }
 56 | }
 57 | 
 58 | impl FunctionCall {
 59 |     /// Returns the function definition referenced by the function call. In practice, it's not
 60 |     /// always the case that the function call will resolve to the referenced declaration. However,
 61 |     /// the type identifier of the real function (possibly overriding function) would be
 62 |     /// conserved i.e the same as the suspected target function
 63 |     ///
 64 |     /// Also see [`FunctionCall::is_internal_call`]
 65 |     #[inline]
 66 |     pub fn suspected_target_function<'a>(
 67 |         &self,
 68 |         context: &'a WorkspaceContext,
 69 |     ) -> Option<&'a FunctionDefinition> {
 70 |         // The most common forms of expressions when making a function call is
 71 |         // 1) xyz()
 72 |         // 2) A.xyz() where A is super or any parent class or library name or a something on which
 73 |         //    library is being used for. (using lib for uint8) .... 6.xyz()
 74 |         match self.expression.as_ref() {
 75 |             Expression::Identifier(Identifier { referenced_declaration: Some(id), .. })
 76 |             | Expression::MemberAccess(MemberAccess { referenced_declaration: Some(id), .. }) => {
 77 |                 if let Some(ASTNode::FunctionDefinition(func)) = context.nodes.get(id) {
 78 |                     Some(func)
 79 |                 } else {
 80 |                     None
 81 |                 }
 82 |             }
 83 |             // TODO: Improve this function heuristics by exhausting enum possibilities for
 84 |             // expression
 85 |             _ => None,
 86 |         }
 87 |     }
 88 |     /// Returns the function definition or variable declaration referenced by the function call.
 89 |     /// Also see [`FunctionCall::suspected_target_function`]
 90 |     #[inline]
 91 |     pub fn suspected_function_selector(&self, context: &WorkspaceContext) -> Option<String> {
 92 |         match self.expression.as_ref() {
 93 |             Expression::Identifier(Identifier { referenced_declaration: Some(id), .. }) => {
 94 |                 if let Some(ASTNode::FunctionDefinition(func)) = context.nodes.get(id) {
 95 |                     func.function_selector.clone()
 96 |                 } else {
 97 |                     None
 98 |                 }
 99 |             }
100 |             Expression::MemberAccess(MemberAccess { referenced_declaration: Some(id), .. }) => {
101 |                 let suspect = context.nodes.get(id)?;
102 |                 match suspect {
103 |                     ASTNode::FunctionDefinition(func) => func.function_selector.clone(),
104 |                     // could be referencing a public state variable (pseudo getter method)
105 |                     ASTNode::VariableDeclaration(var) => var.function_selector.clone(),
106 |                     _ => None,
107 |                 }
108 |             }
109 |             // TODO: Improve this function heuristics by exhausting enum possibilities for
110 |             // expression.
111 |             _ => None,
112 |         }
113 |     }
114 | }
115 | 
116 | impl ModifierInvocation {
117 |     /// Returns the modifier definition referenced by the modifier invocation. In practice, it's not
118 |     /// always the case that the function call will resolve to the referenced declaration. However,
119 |     /// the type identifier of the real modifier (possibly overriding modifier) would be
120 |     /// conserved i.e the same as the suspected target modifier
121 |     #[inline]
122 |     pub fn suspected_target_modifier<'a>(
123 |         &self,
124 |         context: &'a WorkspaceContext,
125 |     ) -> Option<&'a ModifierDefinition> {
126 |         let target_id = self.modifier_name.referenced_declaration()?;
127 |         if let Some(ASTNode::ModifierDefinition(modifier)) = context.nodes.get(&target_id) {
128 |             return Some(modifier);
129 |         }
130 |         None
131 |     }
132 | }
133 | 
```

--------------------------------------------------------------------------------
/tests/ast/documentation_on_statements.json:
--------------------------------------------------------------------------------

```json
1 | {"absolutePath":"a","exportedSymbols":{"C":[27]},"id":28,"nodeType":"SourceUnit","nodes":[{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":27,"linearizedBaseContracts":[27],"name":"C","nameLocation":"9:1:1","nodeType":"ContractDefinition","nodes":[{"constant":false,"id":2,"mutability":"mutable","name":"a","nameLocation":"50:1:1","nodeType":"VariableDeclaration","scope":27,"src":"45:6:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":1,"name":"uint","nodeType":"ElementaryTypeName","src":"45:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"body":{"id":25,"nodeType":"Block","src":"99:229:1","statements":[{"body":{"id":21,"nodeType":"Block","src":"156:66:1","statements":[{"expression":{"id":19,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":17,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":5,"src":"205:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"*=","rightHandSide":{"hexValue":"32","id":18,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"210:1:1","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"205:6:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":20,"nodeType":"ExpressionStatement","src":"205:6:1"}]},"condition":{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":13,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"id":11,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":8,"src":"143:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"<","rightExpression":{"hexValue":"3230","id":12,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"147:2:1","typeDescriptions":{"typeIdentifier":"t_rational_20_by_1","typeString":"int_const 20"},"value":"20"},"src":"143:6:1","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"id":22,"initializationExpression":{"assignments":[8],"declarations":[{"constant":false,"id":8,"mutability":"mutable","name":"i","nameLocation":"136:1:1","nodeType":"VariableDeclaration","scope":22,"src":"131:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":7,"name":"uint","nodeType":"ElementaryTypeName","src":"131:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":10,"initialValue":{"hexValue":"30","id":9,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"140:1:1","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"nodeType":"VariableDeclarationStatement","src":"131:10:1"},"loopExpression":{"expression":{"id":15,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"151:3:1","subExpression":{"id":14,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":8,"src":"151:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":16,"nodeType":"ExpressionStatement","src":"151:3:1"},"nodeType":"ForStatement","src":"126:96:1"},{"expression":{"id":23,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":5,"src":"320:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":6,"id":24,"nodeType":"Return","src":"313:8:1"}]},"functionSelector":"26121ff0","id":26,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"66:1:1","nodeType":"FunctionDefinition","parameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"67:2:1"},"returnParameters":{"id":6,"nodeType":"ParameterList","parameters":[{"constant":false,"id":5,"mutability":"mutable","name":"x","nameLocation":"96:1:1","nodeType":"VariableDeclaration","scope":26,"src":"91:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":4,"name":"uint","nodeType":"ElementaryTypeName","src":"91:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"90:8:1"},"scope":27,"src":"57:271:1","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":28,"src":"0:330:1","usedErrors":[]}],"src":"0:331:1"}
2 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/context/graph/preprocess/legacy.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::collections::{HashMap, HashSet, hash_map};
  2 | 
  3 | use crate::{
  4 |     ast::{Expression, IdentifierOrIdentifierPath, NodeID, NodeType},
  5 |     context::{
  6 |         browser::{ExtractFunctionCalls, ExtractModifierInvocations},
  7 |         workspace::WorkspaceContext,
  8 |     },
  9 | };
 10 | 
 11 | use crate::context::graph::{
 12 |     Error, LegacyWorkspaceCallGraph, RawCallGraph, Result, traits::Transpose,
 13 | };
 14 | 
 15 | impl LegacyWorkspaceCallGraph {
 16 |     /// Formula to create [`WorkspaceCallGraph`] for global preprocessing .
 17 |     pub fn from_context(context: &WorkspaceContext) -> Result<LegacyWorkspaceCallGraph> {
 18 |         let mut raw_callgraph: RawCallGraph = HashMap::new();
 19 |         let mut visited: HashSet<NodeID> = HashSet::new();
 20 | 
 21 |         let funcs = context
 22 |             .function_definitions()
 23 |             .into_iter()
 24 |             .filter(|func| func.implemented)
 25 |             .collect::<Vec<_>>();
 26 | 
 27 |         let modifier_definitions = context.modifier_definitions();
 28 | 
 29 |         for func in funcs {
 30 |             dfs_to_create_graph(func.id, &mut raw_callgraph, &mut visited, context)
 31 |                 .map_err(|_| Error::WorkspaceCallGraphDFSError)?;
 32 |         }
 33 | 
 34 |         for modifier in modifier_definitions {
 35 |             dfs_to_create_graph(modifier.id, &mut raw_callgraph, &mut visited, context)
 36 |                 .map_err(|_| Error::WorkspaceCallGraphDFSError)?;
 37 |         }
 38 | 
 39 |         Ok(LegacyWorkspaceCallGraph { raw_callgraph })
 40 |     }
 41 | }
 42 | 
 43 | /// Make connections from each of the nodes of [`crate::ast::FunctionDefinition`] and
 44 | /// [`crate::ast::ModifierDefinition`] with their connected counterparts.
 45 | fn dfs_to_create_graph(
 46 |     id: NodeID,
 47 |     raw_callgraph: &mut RawCallGraph,
 48 |     visited: &mut HashSet<NodeID>,
 49 |     context: &WorkspaceContext,
 50 | ) -> Result<()> {
 51 |     if visited.contains(&id) {
 52 |         return Ok(());
 53 |     }
 54 | 
 55 |     visited.insert(id);
 56 | 
 57 |     // Only deal with `id`s that are in scope right now
 58 |     if let Some(from_node) = context.nodes.get(&id) {
 59 |         // referenced_declarations from previous calls in the recursion stack need to be vetted
 60 |         if from_node.node_type() != NodeType::FunctionDefinition
 61 |             && from_node.node_type() != NodeType::ModifierDefinition
 62 |         {
 63 |             return Ok(());
 64 |         }
 65 | 
 66 |         // connections to FunctionDefinition
 67 |         let function_calls = ExtractFunctionCalls::from(from_node).extracted;
 68 |         for function_call in function_calls {
 69 |             if let Expression::Identifier(identifier) = function_call.expression.as_ref()
 70 |                 && let Some(referenced_function_id) = identifier.referenced_declaration
 71 |             {
 72 |                 create_connection_if_not_exists(id, referenced_function_id, raw_callgraph);
 73 |                 dfs_to_create_graph(referenced_function_id, raw_callgraph, visited, context)?;
 74 |             }
 75 |         }
 76 | 
 77 |         // connections to ModifierDefinition
 78 |         let modifier_invocations = ExtractModifierInvocations::from(from_node).extracted;
 79 |         for modifier_invocation in &modifier_invocations {
 80 |             match &modifier_invocation.modifier_name {
 81 |                 IdentifierOrIdentifierPath::Identifier(identifier) => {
 82 |                     if let Some(reference_modifier_id) = identifier.referenced_declaration {
 83 |                         create_connection_if_not_exists(id, reference_modifier_id, raw_callgraph);
 84 |                         dfs_to_create_graph(
 85 |                             reference_modifier_id,
 86 |                             raw_callgraph,
 87 |                             visited,
 88 |                             context,
 89 |                         )?;
 90 |                     }
 91 |                 }
 92 |                 IdentifierOrIdentifierPath::IdentifierPath(identifier_path) => {
 93 |                     let referenced_modifier_id = identifier_path.referenced_declaration;
 94 |                     create_connection_if_not_exists(id, referenced_modifier_id, raw_callgraph);
 95 |                     dfs_to_create_graph(referenced_modifier_id, raw_callgraph, visited, context)?;
 96 |                 }
 97 |             }
 98 |         }
 99 |     }
100 | 
101 |     // Change the default return to error later in "strict mode" maybe, because if we
102 |     // can't find the node that means, the file was not in scope and hence it is not
103 |     // available in the context although references to it exist.
104 |     Ok(())
105 | }
106 | 
107 | fn create_connection_if_not_exists(
108 |     from_id: NodeID,
109 |     to_id: NodeID,
110 |     raw_callgraph: &mut RawCallGraph,
111 | ) {
112 |     match raw_callgraph.entry(from_id) {
113 |         hash_map::Entry::Occupied(mut o) => {
114 |             // Performance Tip: Maybe later use binary search (it requires keeping ascending order
115 |             // while inserting tho)
116 |             if !o.get().contains(&to_id) {
117 |                 o.get_mut().push(to_id);
118 |             }
119 |         }
120 |         hash_map::Entry::Vacant(v) => {
121 |             v.insert(vec![to_id]);
122 |         }
123 |     }
124 | }
125 | 
126 | impl Transpose for RawCallGraph {
127 |     fn reverse(&self) -> Self {
128 |         let mut reversed_callgraph = RawCallGraph::default();
129 |         for (from_id, tos) in self {
130 |             for to_id in tos {
131 |                 create_connection_if_not_exists(*to_id, *from_id, &mut reversed_callgraph);
132 |             }
133 |         }
134 |         reversed_callgraph
135 |     }
136 | }
137 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/detect/low/multiple_placeholders.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::{
  2 |     collections::{BTreeMap, HashSet},
  3 |     error::Error,
  4 | };
  5 | 
  6 | use crate::{
  7 |     ast::{NodeID, NodeType},
  8 |     capture,
  9 |     context::{
 10 |         flow::{Cfg, CfgNodeId},
 11 |         workspace::WorkspaceContext,
 12 |     },
 13 |     detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
 14 | };
 15 | use eyre::Result;
 16 | 
 17 | // HOW TO USE THIS TEMPLATE:
 18 | // 1. Copy this file and rename it to the snake_case version of the issue you are detecting.
 19 | // 2. Rename the TemplateDetector struct and impl to your new issue name.
 20 | // 3. Add this file and detector struct to the mod.rs file in the same directory.
 21 | // 4. Implement the detect function to find instances of the issue.
 22 | 
 23 | #[derive(Default)]
 24 | pub struct MultiplePlaceholdersDetector {
 25 |     // Keys are: [0] source file name, [1] line number, [2] character location of node.
 26 |     // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
 27 |     found_instances: BTreeMap<(String, usize, String), NodeID>,
 28 |     hints: BTreeMap<(String, usize, String), String>,
 29 | }
 30 | 
 31 | impl IssueDetector for MultiplePlaceholdersDetector {
 32 |     fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
 33 |         let multiple_placeholders = |cfg: &Cfg, start: CfgNodeId| -> bool {
 34 |             // collect starting points
 35 |             let placeholders: HashSet<CfgNodeId> = {
 36 |                 let mut set: HashSet<CfgNodeId> = Default::default();
 37 |                 fn collect_cfg_nodes(
 38 |                     cfg: &Cfg,
 39 |                     visited: &mut HashSet<CfgNodeId>,
 40 |                     curr_node: CfgNodeId,
 41 |                 ) {
 42 |                     if visited.contains(&curr_node) {
 43 |                         return;
 44 |                     }
 45 |                     visited.insert(curr_node);
 46 | 
 47 |                     for child in curr_node.children(cfg) {
 48 |                         collect_cfg_nodes(cfg, visited, child);
 49 |                     }
 50 |                 }
 51 |                 collect_cfg_nodes(cfg, &mut set, start);
 52 |                 set.into_iter()
 53 |                     .filter(|n| {
 54 |                         cfg.nodes.get(n).is_some_and(|c| {
 55 |                             c.reflect(context)
 56 |                                 .is_some_and(|d| d.node_type() == NodeType::PlaceholderStatement)
 57 |                         })
 58 |                     })
 59 |                     .collect()
 60 |             };
 61 | 
 62 |             fn dfs(
 63 |                 context: &WorkspaceContext,
 64 |                 cfg: &Cfg,
 65 |                 visited: &mut HashSet<CfgNodeId>,
 66 |                 curr_node: CfgNodeId,
 67 |                 count: &mut usize,
 68 |             ) {
 69 |                 if visited.contains(&curr_node) {
 70 |                     return;
 71 |                 }
 72 |                 visited.insert(curr_node);
 73 | 
 74 |                 if cfg.nodes.get(&curr_node).is_some_and(|c| {
 75 |                     c.reflect(context)
 76 |                         .is_some_and(|d| d.node_type() == NodeType::PlaceholderStatement)
 77 |                 }) {
 78 |                     *count += 1
 79 |                 }
 80 | 
 81 |                 for child in curr_node.children(cfg) {
 82 |                     dfs(context, cfg, visited, child, count);
 83 |                 }
 84 |             }
 85 | 
 86 |             for starting_point in placeholders {
 87 |                 let mut visited: HashSet<CfgNodeId> = Default::default();
 88 |                 let mut count = 0;
 89 |                 dfs(context, cfg, &mut visited, starting_point, &mut count);
 90 |                 if count > 1 {
 91 |                     return true;
 92 |                 }
 93 |             }
 94 |             false
 95 |         };
 96 | 
 97 |         for modifier in context.modifier_definitions() {
 98 |             let Some((cfg, start, _)) = Cfg::from_modifier_body(context, modifier) else {
 99 |                 continue;
100 |             };
101 |             if multiple_placeholders(&cfg, start) {
102 |                 capture!(self, context, modifier);
103 |             }
104 |         }
105 |         Ok(!self.found_instances.is_empty())
106 |     }
107 | 
108 |     fn severity(&self) -> IssueSeverity {
109 |         IssueSeverity::Low
110 |     }
111 | 
112 |     fn title(&self) -> String {
113 |         String::from("Multiple Placeholders in Modifier")
114 |     }
115 | 
116 |     fn description(&self) -> String {
117 |         String::from(
118 |             "Design the modifier to only contain 1 placeholder statement. If that is not possible, split the logic into multiple modifiers.",
119 |         )
120 |     }
121 | 
122 |     fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
123 |         self.found_instances.clone()
124 |     }
125 | 
126 |     fn hints(&self) -> BTreeMap<(String, usize, String), String> {
127 |         self.hints.clone()
128 |     }
129 | 
130 |     fn name(&self) -> String {
131 |         IssueDetectorNamePool::MultiplePlaceholders.to_string()
132 |     }
133 | }
134 | 
135 | #[cfg(test)]
136 | mod multiple_placeholder_tests {
137 | 
138 |     use crate::detect::{
139 |         detector::IssueDetector, low::multiple_placeholders::MultiplePlaceholdersDetector,
140 |         test_utils,
141 |     };
142 | 
143 |     #[test]
144 |     fn test_multiple_placeholders() {
145 |         let context = test_utils::load_solidity_source_unit(
146 |             "../tests/contract-playground/src/MultiplePlaceholders.sol",
147 |         );
148 | 
149 |         let mut detector = MultiplePlaceholdersDetector::default();
150 |         let found = detector.detect(&context).unwrap();
151 |         assert!(found);
152 |         assert_eq!(detector.instances().len(), 3);
153 |     }
154 | }
155 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/context/mcp/node_finder/utils.rs:
--------------------------------------------------------------------------------

```rust
  1 | use regex::Regex;
  2 | 
  3 | use crate::{
  4 |     ast::{ASTNode, FunctionKind, NodeID, NodeType},
  5 |     context::{
  6 |         mcp::node_finder::render::{NodeInfo, NodeInfoBuilder},
  7 |         workspace::WorkspaceContext,
  8 |     },
  9 | };
 10 | 
 11 | // Matches functions, modifiers and contracts by their exact names.
 12 | 
 13 | #[inline]
 14 | pub fn get_matching_functions(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
 15 |     get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::FunctionDefinition)
 16 | }
 17 | 
 18 | #[inline]
 19 | pub fn get_matching_modifiers(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
 20 |     get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::ModifierDefinition)
 21 | }
 22 | 
 23 | #[inline]
 24 | pub fn get_matching_contracts(idx: usize, context: &WorkspaceContext, name: &str) -> Vec<NodeInfo> {
 25 |     get_nodes_by_exact_name_match(idx, context, Some(name), NodeType::ContractDefinition)
 26 | }
 27 | 
 28 | // Matches all events and errors.
 29 | 
 30 | #[inline]
 31 | pub fn get_all_events(idx: usize, context: &WorkspaceContext) -> Vec<NodeInfo> {
 32 |     get_nodes_by_exact_name_match(idx, context, None, NodeType::EventDefinition)
 33 | }
 34 | 
 35 | #[inline]
 36 | pub fn get_all_errors(idx: usize, context: &WorkspaceContext) -> Vec<NodeInfo> {
 37 |     get_nodes_by_exact_name_match(idx, context, None, NodeType::ErrorDefinition)
 38 | }
 39 | 
 40 | // Matches functions, modifiers and state variables whose code snippets match a given regex
 41 | 
 42 | #[inline]
 43 | pub fn grep_functions(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
 44 |     let regex = Regex::new(term).expect("invalid regex was passed");
 45 |     context
 46 |         .function_definitions()
 47 |         .into_iter()
 48 |         .filter(|&f| {
 49 |             let f: ASTNode = f.into();
 50 |             let code_snippet = context.get_code_snippet(&f);
 51 |             regex.is_match(&code_snippet)
 52 |         })
 53 |         .map(|func| {
 54 |             let func_kind = func.kind();
 55 |             let mut name = func.name.to_owned();
 56 | 
 57 |             if func_kind != &FunctionKind::Function {
 58 |                 if name.is_empty() {
 59 |                     name = func_kind.to_string();
 60 |                 } else {
 61 |                     name.push_str(&format!("( {} )", func_kind));
 62 |                 }
 63 |             }
 64 | 
 65 |             NodeInfoBuilder::default()
 66 |                 .name(name)
 67 |                 .node_id(func.id)
 68 |                 .compilation_unit_index(idx)
 69 |                 .build()
 70 |                 .expect("failed to build node info")
 71 |         })
 72 |         .collect()
 73 | }
 74 | 
 75 | #[inline]
 76 | pub fn grep_modifiers(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
 77 |     let regex = Regex::new(term).expect("invalid regex was passed");
 78 |     context
 79 |         .modifier_definitions()
 80 |         .into_iter()
 81 |         .filter(|&m| {
 82 |             let m: ASTNode = m.into();
 83 |             let code_snippet = context.get_code_snippet(&m);
 84 |             regex.is_match(&code_snippet)
 85 |         })
 86 |         .map(|modifier| {
 87 |             NodeInfoBuilder::default()
 88 |                 .name(modifier.name.to_owned())
 89 |                 .node_id(modifier.id)
 90 |                 .compilation_unit_index(idx)
 91 |                 .build()
 92 |                 .expect("failed to build node info")
 93 |         })
 94 |         .collect()
 95 | }
 96 | 
 97 | #[inline]
 98 | pub fn grep_state_variables(idx: usize, context: &WorkspaceContext, term: &str) -> Vec<NodeInfo> {
 99 |     let regex = Regex::new(term).expect("invalid regex was passed");
100 |     context
101 |         .contract_definitions()
102 |         .into_iter()
103 |         .flat_map(|c| c.top_level_variables())
104 |         .filter(|v| regex.is_match(&v.name))
105 |         .map(|v| {
106 |             NodeInfoBuilder::default()
107 |                 .name(v.name.to_string())
108 |                 .node_id(v.id)
109 |                 .compilation_unit_index(idx)
110 |                 .build()
111 |                 .expect("failed to build node info")
112 |         })
113 |         .collect()
114 | }
115 | 
116 | // Helper functions
117 | 
118 | fn get_nodes_by_exact_name_match(
119 |     compilation_unit_index: usize,
120 |     context: &WorkspaceContext,
121 |     search_term: Option<&str>,
122 |     node_ty: NodeType,
123 | ) -> Vec<NodeInfo> {
124 |     let mut matching_nodes = vec![];
125 | 
126 |     let mut add_node = |name: &str, id: NodeID| {
127 |         if let Ok(node_info) = NodeInfoBuilder::default()
128 |             .name(name.to_string())
129 |             .node_id(id)
130 |             .compilation_unit_index(compilation_unit_index)
131 |             .build()
132 |         {
133 |             matching_nodes.push(node_info);
134 |         }
135 |     };
136 | 
137 |     match node_ty {
138 |         NodeType::ContractDefinition => {
139 |             context
140 |                 .contract_definitions()
141 |                 .iter()
142 |                 .filter(|m| search_term.is_none_or(|t| t == m.name))
143 |                 .for_each(|m| add_node(&m.name, m.id));
144 |         }
145 |         NodeType::FunctionDefinition => {
146 |             context
147 |                 .function_definitions()
148 |                 .iter()
149 |                 .filter(|m| search_term.is_none_or(|t| t == m.name))
150 |                 .for_each(|m| add_node(&m.name, m.id));
151 |         }
152 |         NodeType::ModifierDefinition => {
153 |             context
154 |                 .modifier_definitions()
155 |                 .iter()
156 |                 .filter(|m| search_term.is_none_or(|t| t == m.name))
157 |                 .for_each(|m| add_node(&m.name, m.id));
158 |         }
159 |         _ => {}
160 |     };
161 |     matching_nodes
162 | }
163 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/nested_struct_in_mapping.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::{collections::BTreeMap, error::Error};
  2 | 
  3 | use crate::ast::{NodeID, NodeType, TypeName};
  4 | 
  5 | use crate::{
  6 |     capture,
  7 |     context::{
  8 |         browser::ExtractPragmaDirectives,
  9 |         workspace::{ASTNode, WorkspaceContext},
 10 |     },
 11 |     detect::{
 12 |         detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
 13 |         helpers::pragma_directive_to_semver,
 14 |     },
 15 | };
 16 | use eyre::Result;
 17 | use semver::VersionReq;
 18 | 
 19 | #[derive(Default)]
 20 | pub struct NestedStructInMappingDetector {
 21 |     // Keys are: [0] source file name, [1] line number, [2] character location of node.
 22 |     // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
 23 |     found_instances: BTreeMap<(String, usize, String), NodeID>,
 24 | }
 25 | 
 26 | fn version_req_allows_below_0_5_0(version_req: &VersionReq) -> bool {
 27 |     if version_req.comparators.is_empty() {
 28 |         return false; // Return false or handle as needed if there are no comparators
 29 |     }
 30 | 
 31 |     let comparator = &version_req.comparators[0];
 32 |     comparator.major == 0 && comparator.minor.is_some_and(|m| m < 5)
 33 | }
 34 | 
 35 | impl IssueDetector for NestedStructInMappingDetector {
 36 |     fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
 37 |         // When you have found an instance of the issue,
 38 |         // use the following macro to add it to `found_instances`:
 39 |         //
 40 |         // capture!(self, context, item);
 41 | 
 42 |         let mappings = context.variable_declarations().into_iter().filter(|vd| {
 43 |             if let Some(type_name) = &vd.type_name {
 44 |                 if let TypeName::Mapping(_) = type_name {
 45 |                     return true;
 46 |                 }
 47 |                 return false;
 48 |             }
 49 |             false
 50 |         });
 51 | 
 52 |         for mapping in mappings {
 53 |             if let Some(TypeName::Mapping(mapping_type)) = &mapping.type_name
 54 |                 && let TypeName::UserDefinedTypeName(user_defined_type) = &*mapping_type.value_type
 55 |             {
 56 |                 let struct_definition_ast_node =
 57 |                     context.nodes.get(&user_defined_type.referenced_declaration);
 58 |                 if let Some(ASTNode::StructDefinition(struct_definition)) =
 59 |                     struct_definition_ast_node
 60 |                 {
 61 |                     for member in struct_definition.members.iter() {
 62 |                         if let Some(member_type_string) = &member.type_descriptions.type_string
 63 |                             && member_type_string.contains("struct")
 64 |                         {
 65 |                             // Check if the contract that this is in allows for solidity
 66 |                             // pragma below 0.5.0
 67 |                             let source_unit_ast_node =
 68 |                                 context.get_closest_ancestor(mapping.id, NodeType::SourceUnit);
 69 |                             if let Some(source_unit_ast_node) = source_unit_ast_node {
 70 |                                 let pragma_directives =
 71 |                                     ExtractPragmaDirectives::from(source_unit_ast_node).extracted;
 72 |                                 let version_req =
 73 |                                     pragma_directive_to_semver(pragma_directives.first().unwrap())?;
 74 |                                 if version_req_allows_below_0_5_0(&version_req) {
 75 |                                     capture!(self, context, mapping);
 76 |                                 }
 77 |                             }
 78 |                         }
 79 |                     }
 80 |                 }
 81 |             }
 82 |         }
 83 | 
 84 |         Ok(!self.found_instances.is_empty())
 85 |     }
 86 | 
 87 |     fn severity(&self) -> IssueSeverity {
 88 |         IssueSeverity::High
 89 |     }
 90 | 
 91 |     fn title(&self) -> String {
 92 |         String::from("Nested Structs in Mappings pre-0.5.0")
 93 |     }
 94 | 
 95 |     fn description(&self) -> String {
 96 |         String::from(
 97 |             "Prior to updates in Solidity 0.5.0, public mappings with nested structs compiled, but produced incorrect values. Refrain from using these, or update to a more recent version of Solidity.",
 98 |         )
 99 |     }
100 | 
101 |     fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
102 |         self.found_instances.clone()
103 |     }
104 | 
105 |     fn name(&self) -> String {
106 |         IssueDetectorNamePool::NestedStructInMapping.to_string()
107 |     }
108 | }
109 | 
110 | #[cfg(test)]
111 | mod nested_struct_in_mapping_detector_tests {
112 | 
113 |     use crate::detect::{detector::IssueDetector, high::NestedStructInMappingDetector};
114 | 
115 |     #[test]
116 | 
117 |     fn test_nested_struct_in_mapping_detector() {
118 |         let context = crate::detect::test_utils::load_solidity_source_unit(
119 |             "../tests/contract-playground/src/nested_mappings/NestedMappings.sol",
120 |         );
121 | 
122 |         let mut detector = NestedStructInMappingDetector::default();
123 |         let found = detector.detect(&context).unwrap();
124 |         assert!(found);
125 |         assert_eq!(detector.instances().len(), 1);
126 |     }
127 | 
128 |     #[test]
129 | 
130 |     fn test_nested_struct_in_mapping_detector_no_issue() {
131 |         let context = crate::detect::test_utils::load_solidity_source_unit(
132 |             "../tests/contract-playground/src/nested_mappings/LaterVersion.sol",
133 |         );
134 | 
135 |         let mut detector = NestedStructInMappingDetector::default();
136 |         let found = detector.detect(&context).unwrap();
137 |         assert!(!found);
138 |         assert_eq!(detector.instances().len(), 0);
139 |     }
140 | }
141 | 
```

--------------------------------------------------------------------------------
/tests/ast/documentation_triple.json:
--------------------------------------------------------------------------------

```json
1 | {"absolutePath":"a","exportedSymbols":{"C":[28]},"id":29,"nodeType":"SourceUnit","nodes":[{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":28,"linearizedBaseContracts":[28],"name":"C","nameLocation":"9:1:1","nodeType":"ContractDefinition","nodes":[{"constant":false,"documentation":{"id":1,"nodeType":"StructuredDocumentation","src":"17:8:1","text":"test"},"id":3,"mutability":"mutable","name":"a","nameLocation":"35:1:1","nodeType":"VariableDeclaration","scope":28,"src":"30:6:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":2,"name":"uint","nodeType":"ElementaryTypeName","src":"30:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"body":{"id":26,"nodeType":"Block","src":"84:181:1","statements":[{"body":{"id":22,"nodeType":"Block","src":"142:75:1","statements":[{"documentation":"tee\n s \"t\" 3","expression":{"id":20,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":18,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":6,"src":"200:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"*=","rightHandSide":{"hexValue":"32","id":19,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"205:1:1","typeDescriptions":{"typeIdentifier":"t_rational_2_by_1","typeString":"int_const 2"},"value":"2"},"src":"200:6:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":21,"nodeType":"ExpressionStatement","src":"200:6:1"}]},"condition":{"commonType":{"typeIdentifier":"t_uint256","typeString":"uint256"},"id":14,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftExpression":{"id":12,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":9,"src":"129:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"BinaryOperation","operator":"<","rightExpression":{"hexValue":"3230","id":13,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"133:2:1","typeDescriptions":{"typeIdentifier":"t_rational_20_by_1","typeString":"int_const 20"},"value":"20"},"src":"129:6:1","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"documentation":"test2","id":23,"initializationExpression":{"assignments":[9],"declarations":[{"constant":false,"id":9,"mutability":"mutable","name":"i","nameLocation":"122:1:1","nodeType":"VariableDeclaration","scope":23,"src":"117:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":8,"name":"uint","nodeType":"ElementaryTypeName","src":"117:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"id":11,"initialValue":{"hexValue":"30","id":10,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"126:1:1","typeDescriptions":{"typeIdentifier":"t_rational_0_by_1","typeString":"int_const 0"},"value":"0"},"nodeType":"VariableDeclarationStatement","src":"117:10:1"},"loopExpression":{"expression":{"id":16,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"nodeType":"UnaryOperation","operator":"++","prefix":false,"src":"137:3:1","subExpression":{"id":15,"name":"i","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":9,"src":"137:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":17,"nodeType":"ExpressionStatement","src":"137:3:1"},"nodeType":"ForStatement","src":"112:105:1"},{"documentation":"tes \"t4\" ","expression":{"id":24,"name":"x","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":6,"src":"257:1:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"functionReturnParameters":7,"id":25,"nodeType":"Return","src":"250:8:1"}]},"functionSelector":"26121ff0","id":27,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"51:1:1","nodeType":"FunctionDefinition","parameters":{"id":4,"nodeType":"ParameterList","parameters":[],"src":"52:2:1"},"returnParameters":{"id":7,"nodeType":"ParameterList","parameters":[{"constant":false,"id":6,"mutability":"mutable","name":"x","nameLocation":"81:1:1","nodeType":"VariableDeclaration","scope":27,"src":"76:6:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":5,"name":"uint","nodeType":"ElementaryTypeName","src":"76:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"75:8:1"},"scope":28,"src":"42:223:1","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":29,"src":"0:267:1","usedErrors":[]}],"src":"0:268:1"}
2 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/context/mcp/node_summarizer/utils.rs:
--------------------------------------------------------------------------------

```rust
  1 | use crate::{
  2 |     ast::{ASTNode, NodeID, NodeType},
  3 |     context::{
  4 |         graph::RawCallGraph,
  5 |         mcp::node_summarizer::render::{
  6 |             EntrypointCallgraphInfo, EntrypointCallgraphInfoBuilder, NodeInfo, NodeInfoBuilder,
  7 |         },
  8 |         workspace::WorkspaceContext,
  9 |     },
 10 | };
 11 | use std::collections::{HashMap, HashSet, hash_map::Entry};
 12 | 
 13 | pub fn get_containing_contract(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
 14 |     if let ASTNode::ContractDefinition(_) = node {
 15 |         return None;
 16 |     }
 17 |     let Some(ASTNode::ContractDefinition(parent_contract)) = context.get_closest_ancestor(
 18 |         node.id().expect("node found without an ID"),
 19 |         NodeType::ContractDefinition,
 20 |     ) else {
 21 |         return None;
 22 |     };
 23 |     Some((parent_contract.id, parent_contract.name.clone()).into())
 24 | }
 25 | 
 26 | pub fn get_containing_modifier(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
 27 |     if let ASTNode::ModifierDefinition(_) = node {
 28 |         return None;
 29 |     }
 30 |     let Some(ASTNode::ModifierDefinition(parent_modifier)) = context.get_closest_ancestor(
 31 |         node.id().expect("node found without an ID"),
 32 |         NodeType::ModifierDefinition,
 33 |     ) else {
 34 |         return None;
 35 |     };
 36 |     Some((parent_modifier.id, parent_modifier.name.clone()).into())
 37 | }
 38 | 
 39 | pub fn get_containing_function(context: &WorkspaceContext, node: &ASTNode) -> Option<NodeInfo> {
 40 |     if let ASTNode::FunctionDefinition(_) = node {
 41 |         return None;
 42 |     }
 43 |     let Some(ASTNode::FunctionDefinition(parent_function)) = context.get_closest_ancestor(
 44 |         node.id().expect("node found without an ID"),
 45 |         NodeType::FunctionDefinition,
 46 |     ) else {
 47 |         return None;
 48 |     };
 49 |     Some((parent_function.id, parent_function.name.clone()).into())
 50 | }
 51 | 
 52 | impl From<(NodeID, String)> for NodeInfo {
 53 |     fn from(value: (NodeID, String)) -> Self {
 54 |         NodeInfoBuilder::default()
 55 |             .name(value.1)
 56 |             .node_id(value.0)
 57 |             .build()
 58 |             .expect("failed to build node info")
 59 |     }
 60 | }
 61 | 
 62 | pub fn get_containing_callgraphs(
 63 |     context: &WorkspaceContext,
 64 |     node: &ASTNode,
 65 | ) -> Vec<EntrypointCallgraphInfo> {
 66 |     // Given node, we want to locate it in the callgraph
 67 |     let node_id = node.id().expect("node found without an ID");
 68 | 
 69 |     let parent_graph_node = match (
 70 |         context.get_closest_ancestor_including_self(node_id, NodeType::FunctionDefinition),
 71 |         context.get_closest_ancestor_including_self(node_id, NodeType::ModifierDefinition),
 72 |     ) {
 73 |         (Some(ASTNode::FunctionDefinition(func)), _) => Some(func.id),
 74 |         (_, Some(ASTNode::ModifierDefinition(modifier))) => Some(modifier.id),
 75 |         (_, _) => None,
 76 |     };
 77 | 
 78 |     let Some(parent_graph_node) = parent_graph_node else {
 79 |         return vec![];
 80 |     };
 81 | 
 82 |     // Keys => (deployabele contract id, contract name), Values => entrypoint function ids
 83 |     let mut entrypoint_info: HashMap<(NodeID, String), Vec<NodeID>> = HashMap::new();
 84 | 
 85 |     for contract in context.deployable_contracts() {
 86 |         let Some(contract_callgraph) =
 87 |             context.callgraphs.as_ref().and_then(|c| c.outward_callgraphs.get(&contract.id))
 88 |         else {
 89 |             continue;
 90 |         };
 91 |         let Some(entrypoint_ids) = contract
 92 |             .entrypoint_functions(context)
 93 |             .map(|funcs| funcs.into_iter().map(|f| f.id).collect::<HashSet<_>>())
 94 |         else {
 95 |             continue;
 96 |         };
 97 |         let reachable_entrypoints = traverse_cg_and_get_reachable_entrypoints(
 98 |             parent_graph_node,
 99 |             contract_callgraph,
100 |             &entrypoint_ids,
101 |         );
102 | 
103 |         for entrypoint_id in reachable_entrypoints {
104 |             match entrypoint_info.entry((contract.id, contract.name.to_owned())) {
105 |                 Entry::Occupied(mut o) => {
106 |                     o.get_mut().push(entrypoint_id);
107 |                 }
108 |                 Entry::Vacant(v) => {
109 |                     v.insert(vec![entrypoint_id]);
110 |                 }
111 |             }
112 |         }
113 |     }
114 | 
115 |     entrypoint_info
116 |         .into_iter()
117 |         .map(|((contract_id, contract_name), entrypoints)| {
118 |             EntrypointCallgraphInfoBuilder::default()
119 |                 .deployable_contract_id(contract_id)
120 |                 .deployable_contract_name(contract_name)
121 |                 .entrypoint_ids(entrypoints)
122 |                 .build()
123 |                 .expect("failed to build entrypoint callgraph info")
124 |         })
125 |         .collect()
126 | }
127 | 
128 | fn traverse_cg_and_get_reachable_entrypoints(
129 |     node_id: NodeID,
130 |     outward_cg: &RawCallGraph,
131 |     entrypoint_ids: &HashSet<NodeID>,
132 | ) -> HashSet<NodeID> {
133 |     // Visit all possible nodes starting from node_id in the outward callgraph. Then collect all the
134 |     // nodes which can be potential starting points that lead to node_id in the (real) inward
135 |     // callgraph.
136 |     let mut worklist = vec![node_id];
137 |     let mut visited: HashSet<NodeID> = Default::default();
138 | 
139 |     while let Some(node) = worklist.pop() {
140 |         if visited.contains(&node) {
141 |             continue;
142 |         }
143 |         visited.insert(node);
144 | 
145 |         if let Some(connections) = outward_cg.get(&node) {
146 |             for conn in connections {
147 |                 worklist.push(*conn);
148 |             }
149 |         }
150 |     }
151 | 
152 |     visited.into_iter().filter(|f| entrypoint_ids.contains(f)).collect()
153 | }
154 | 
```

--------------------------------------------------------------------------------
/benchmarks/unsafe-erc20-functions/report/relative_regression_small.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="450" height="300" viewBox="0 0 450 300" xmlns="http://www.w3.org/2000/svg">
 2 | <text x="15" y="130" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 15, 130)">
 3 | Total sample time (ms)
 4 | </text>
 5 | <text x="255" y="285" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
 6 | Iterations (x 10^3)
 7 | </text>
 8 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="75" y2="15"/>
 9 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="139" y1="244" x2="139" y2="15"/>
10 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="203" y1="244" x2="203" y2="15"/>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="267" y1="244" x2="267" y2="15"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="331" y1="244" x2="331" y2="15"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="395" y1="244" x2="395" y2="15"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="434" y2="244"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="208" x2="434" y2="208"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="171" x2="434" y2="171"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="134" x2="434" y2="134"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="97" x2="434" y2="97"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="61" x2="434" y2="61"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="24" x2="434" y2="24"/>
21 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="74,15 74,244 "/>
22 | <text x="65" y="244" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
23 | 0.0
24 | </text>
25 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,244 74,244 "/>
26 | <text x="65" y="208" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
27 | 20.0
28 | </text>
29 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,208 74,208 "/>
30 | <text x="65" y="171" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
31 | 40.0
32 | </text>
33 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,171 74,171 "/>
34 | <text x="65" y="134" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
35 | 60.0
36 | </text>
37 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,134 74,134 "/>
38 | <text x="65" y="97" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
39 | 80.0
40 | </text>
41 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,97 74,97 "/>
42 | <text x="65" y="61" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
43 | 100.0
44 | </text>
45 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,61 74,61 "/>
46 | <text x="65" y="24" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
47 | 120.0
48 | </text>
49 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,24 74,24 "/>
50 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 434,245 "/>
51 | <text x="75" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
52 | 0
53 | </text>
54 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 75,250 "/>
55 | <text x="139" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
56 | 0.5
57 | </text>
58 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="139,245 139,250 "/>
59 | <text x="203" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
60 | 1
61 | </text>
62 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="203,245 203,250 "/>
63 | <text x="267" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
64 | 1.5
65 | </text>
66 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="267,245 267,250 "/>
67 | <text x="331" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
68 | 2
69 | </text>
70 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="331,245 331,250 "/>
71 | <text x="395" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
72 | 2.5
73 | </text>
74 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="395,245 395,250 "/>
75 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="75,244 434,60 "/>
76 | <polygon opacity="0.25" fill="#E31A1C" points="75,244 434,60 434,60 "/>
77 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="75,244 434,15 "/>
78 | <polygon opacity="0.25" fill="#1F78B4" points="75,244 434,19 434,15 "/>
79 | </svg>
80 | 
```

--------------------------------------------------------------------------------
/benchmarks/unspecific-solidity-pragma/report/relative_regression_small.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="450" height="300" viewBox="0 0 450 300" xmlns="http://www.w3.org/2000/svg">
 2 | <text x="15" y="130" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 15, 130)">
 3 | Total sample time (ms)
 4 | </text>
 5 | <text x="255" y="285" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
 6 | Iterations (x 10^3)
 7 | </text>
 8 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="75" y2="15"/>
 9 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="133" y1="244" x2="133" y2="15"/>
10 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="192" y1="244" x2="192" y2="15"/>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="251" y1="244" x2="251" y2="15"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="310" y1="244" x2="310" y2="15"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="369" y1="244" x2="369" y2="15"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="428" y1="244" x2="428" y2="15"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="244" x2="434" y2="244"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="203" x2="434" y2="203"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="162" x2="434" y2="162"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="120" x2="434" y2="120"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="79" x2="434" y2="79"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="75" y1="37" x2="434" y2="37"/>
21 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="74,15 74,244 "/>
22 | <text x="65" y="244" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
23 | 0.0
24 | </text>
25 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,244 74,244 "/>
26 | <text x="65" y="203" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
27 | 20.0
28 | </text>
29 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,203 74,203 "/>
30 | <text x="65" y="162" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
31 | 40.0
32 | </text>
33 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,162 74,162 "/>
34 | <text x="65" y="120" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
35 | 60.0
36 | </text>
37 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,120 74,120 "/>
38 | <text x="65" y="79" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
39 | 80.0
40 | </text>
41 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,79 74,79 "/>
42 | <text x="65" y="37" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
43 | 100.0
44 | </text>
45 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="69,37 74,37 "/>
46 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 434,245 "/>
47 | <text x="75" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
48 | 0
49 | </text>
50 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="75,245 75,250 "/>
51 | <text x="133" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
52 | 1
53 | </text>
54 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="133,245 133,250 "/>
55 | <text x="192" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
56 | 2
57 | </text>
58 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="192,245 192,250 "/>
59 | <text x="251" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
60 | 3
61 | </text>
62 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="251,245 251,250 "/>
63 | <text x="310" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
64 | 4
65 | </text>
66 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="310,245 310,250 "/>
67 | <text x="369" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
68 | 5
69 | </text>
70 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="369,245 369,250 "/>
71 | <text x="428" y="255" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
72 | 6
73 | </text>
74 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="428,245 428,250 "/>
75 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="75,244 434,15 "/>
76 | <polygon opacity="0.25" fill="#E31A1C" points="75,244 434,21 434,15 "/>
77 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="75,244 434,40 "/>
78 | <polygon opacity="0.25" fill="#1F78B4" points="75,244 434,41 434,38 "/>
79 | </svg>
80 | 
```

--------------------------------------------------------------------------------
/benchmarks/push-zero-opcode/report/both/regression.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="960" height="540" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
 2 | <text x="480" y="32" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="16.129032258064516" opacity="1" fill="#000000">
 3 | push-zero-opcode
 4 | </text>
 5 | <text x="27" y="263" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 27, 263)">
 6 | Total sample time (ms)
 7 | </text>
 8 | <text x="510" y="513" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
 9 | Iterations (x 10^3)
10 | </text>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="87" y2="53"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="279" y1="472" x2="279" y2="53"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="471" y1="472" x2="471" y2="53"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="663" y1="472" x2="663" y2="53"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="855" y1="472" x2="855" y2="53"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="932" y2="472"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="397" x2="932" y2="397"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="321" x2="932" y2="321"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="245" x2="932" y2="245"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="169" x2="932" y2="169"/>
21 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="93" x2="932" y2="93"/>
22 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="86,53 86,472 "/>
23 | <text x="77" y="472" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
24 | 0.0
25 | </text>
26 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,472 86,472 "/>
27 | <text x="77" y="397" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
28 | 20.0
29 | </text>
30 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,397 86,397 "/>
31 | <text x="77" y="321" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
32 | 40.0
33 | </text>
34 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,321 86,321 "/>
35 | <text x="77" y="245" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
36 | 60.0
37 | </text>
38 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,245 86,245 "/>
39 | <text x="77" y="169" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
40 | 80.0
41 | </text>
42 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,169 86,169 "/>
43 | <text x="77" y="93" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
44 | 100.0
45 | </text>
46 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,93 86,93 "/>
47 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 932,473 "/>
48 | <text x="87" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
49 | 0
50 | </text>
51 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 87,478 "/>
52 | <text x="279" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
53 | 0.5
54 | </text>
55 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="279,473 279,478 "/>
56 | <text x="471" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
57 | 1
58 | </text>
59 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="471,473 471,478 "/>
60 | <text x="663" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
61 | 1.5
62 | </text>
63 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="663,473 663,478 "/>
64 | <text x="855" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
65 | 2
66 | </text>
67 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="855,473 855,478 "/>
68 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="87,472 932,53 "/>
69 | <polygon opacity="0.25" fill="#E31A1C" points="87,472 932,68 932,53 "/>
70 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="87,472 932,119 "/>
71 | <polygon opacity="0.25" fill="#1F78B4" points="87,472 932,128 932,108 "/>
72 | <text x="132" y="68" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
73 | Base Sample
74 | </text>
75 | <text x="132" y="83" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
76 | New Sample
77 | </text>
78 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="2" points="102,73 122,73 "/>
79 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="2" points="102,88 122,88 "/>
80 | </svg>
81 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/const_func_changes_state.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::{collections::BTreeMap, error::Error};
  2 | 
  3 | use crate::ast::{NodeID, StateMutability};
  4 | 
  5 | use crate::{
  6 |     capture,
  7 |     context::{
  8 |         browser::ApproximateStorageChangeFinder,
  9 |         graph::{CallGraphConsumer, CallGraphDirection, CallGraphVisitor},
 10 |         workspace::WorkspaceContext,
 11 |     },
 12 |     detect::{
 13 |         detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
 14 |         helpers,
 15 |     },
 16 | };
 17 | use eyre::Result;
 18 | 
 19 | #[derive(Default)]
 20 | pub struct ConstantFunctionChangesStateDetector {
 21 |     // Keys are: [0] source file name, [1] line number, [2] character location of node.
 22 |     // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
 23 |     found_instances: BTreeMap<(String, usize, String), NodeID>,
 24 | }
 25 | 
 26 | impl IssueDetector for ConstantFunctionChangesStateDetector {
 27 |     fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
 28 |         for func in helpers::get_implemented_external_and_public_functions(context) {
 29 |             // Rule applies to only view functions, so ignore the rest
 30 |             if func.state_mutability() != &StateMutability::View {
 31 |                 continue;
 32 |             }
 33 |             // Check if this func is compilable for solc < 0.5.0. If not, move on to the next
 34 |             if !func.compiles_for_solc_below_0_5_0(context) {
 35 |                 continue;
 36 |             }
 37 |             // Now, investigate the function to see if there is scope for any state variable changes
 38 |             let mut tracker = StateVariableChangeTracker { state_var_has_changed: false, context };
 39 | 
 40 |             // Keep legacy for this because it is for solc version below 0.5.0 and the function
 41 |             // selectors don't exist
 42 |             let callgraph = CallGraphConsumer::get_legacy(
 43 |                 context,
 44 |                 &[&(func.into())],
 45 |                 CallGraphDirection::Inward,
 46 |             )?;
 47 |             callgraph.accept(context, &mut tracker)?;
 48 | 
 49 |             if tracker.state_var_has_changed {
 50 |                 capture!(self, context, func);
 51 |             }
 52 |         }
 53 | 
 54 |         Ok(!self.found_instances.is_empty())
 55 |     }
 56 | 
 57 |     fn severity(&self) -> IssueSeverity {
 58 |         IssueSeverity::High
 59 |     }
 60 | 
 61 |     fn title(&self) -> String {
 62 |         String::from("Constant functions changes state")
 63 |     }
 64 | 
 65 |     fn description(&self) -> String {
 66 |         String::from(
 67 |             "Function is declared constant/view but it changes state. Ensure that the attributes of contract compiled prior to 0.5 are correct.",
 68 |         )
 69 |     }
 70 | 
 71 |     fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
 72 |         self.found_instances.clone()
 73 |     }
 74 | 
 75 |     fn name(&self) -> String {
 76 |         IssueDetectorNamePool::ConstantFunctionChangesState.to_string()
 77 |     }
 78 | }
 79 | 
 80 | struct StateVariableChangeTracker<'a> {
 81 |     state_var_has_changed: bool,
 82 |     context: &'a WorkspaceContext,
 83 | }
 84 | 
 85 | impl CallGraphVisitor for StateVariableChangeTracker<'_> {
 86 |     fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> {
 87 |         if self.state_var_has_changed {
 88 |             return Ok(());
 89 |         }
 90 |         // Check for state variable changes
 91 |         let finder = ApproximateStorageChangeFinder::from(self.context, node);
 92 |         if finder.state_variables_have_been_manipulated() {
 93 |             self.state_var_has_changed = true;
 94 |         }
 95 |         Ok(())
 96 |     }
 97 | }
 98 | 
 99 | mod func_compilation_solc_pragma_helper {
100 |     use std::str::FromStr;
101 | 
102 |     use semver::{Version, VersionReq};
103 | 
104 |     use crate::{
105 |         ast::{FunctionDefinition, NodeType},
106 |         context::{
107 |             browser::{ExtractPragmaDirectives, GetClosestAncestorOfTypeX},
108 |             workspace::WorkspaceContext,
109 |         },
110 |         detect::helpers,
111 |     };
112 | 
113 |     impl FunctionDefinition {
114 |         pub fn compiles_for_solc_below_0_5_0(&self, context: &WorkspaceContext) -> bool {
115 |             if let Some(source_unit) = self.closest_ancestor_of_type(context, NodeType::SourceUnit)
116 |             {
117 |                 let pragma_directives = ExtractPragmaDirectives::from(source_unit).extracted;
118 | 
119 |                 if let Some(pragma_directive) = pragma_directives.first()
120 |                     && let Ok(pragma_semver) = helpers::pragma_directive_to_semver(pragma_directive)
121 |                     && version_req_allows_below_0_5_0(&pragma_semver)
122 |                 {
123 |                     return true;
124 |                 }
125 |             }
126 |             false
127 |         }
128 |     }
129 | 
130 |     fn version_req_allows_below_0_5_0(version_req: &VersionReq) -> bool {
131 |         // If it matches any 0.4.0 to 0.4.26, return true
132 |         for i in 0..=26 {
133 |             let version = Version::from_str(&format!("0.4.{}", i)).unwrap();
134 |             if version_req.matches(&version) {
135 |                 return true;
136 |             }
137 |         }
138 | 
139 |         // Else, return false
140 |         false
141 |     }
142 | }
143 | 
144 | #[cfg(test)]
145 | mod constant_func_changing_state {
146 | 
147 |     use crate::detect::{
148 |         detector::IssueDetector,
149 |         high::const_func_changes_state::ConstantFunctionChangesStateDetector,
150 |     };
151 | 
152 |     #[test]
153 | 
154 |     fn test_constant_function_changing_state() {
155 |         let context = crate::detect::test_utils::load_solidity_source_unit(
156 |             "../tests/contract-playground/src/ConstFuncChangeState.sol",
157 |         );
158 | 
159 |         let mut detector = ConstantFunctionChangesStateDetector::default();
160 |         let found = detector.detect(&context).unwrap();
161 |         assert!(found);
162 |         assert_eq!(detector.instances().len(), 1);
163 |     }
164 | }
165 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/detect/high/tx_origin_used_for_auth.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::{collections::BTreeMap, error::Error};
  2 | 
  3 | use crate::ast::{ASTNode, Expression, Identifier, NodeID};
  4 | 
  5 | use crate::{
  6 |     capture,
  7 |     context::{
  8 |         browser::ExtractMemberAccesses,
  9 |         graph::{CallGraphConsumer, CallGraphDirection, CallGraphVisitor},
 10 |         workspace::WorkspaceContext,
 11 |     },
 12 |     detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
 13 | };
 14 | use eyre::Result;
 15 | 
 16 | #[derive(Default)]
 17 | pub struct TxOriginUsedForAuthDetector {
 18 |     // Keys are: [0] source file name, [1] line number, [2] character location of node.
 19 |     // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
 20 |     found_instances: BTreeMap<(String, usize, String), NodeID>,
 21 | }
 22 | 
 23 | impl IssueDetector for TxOriginUsedForAuthDetector {
 24 |     fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
 25 |         for if_statement in context.if_statements() {
 26 |             // Check within the condition block only
 27 |             let ast_node: ASTNode = if_statement.condition.clone().into();
 28 |             self.check_eligibility_and_capture(context, &[&ast_node], &(if_statement.into()))?;
 29 |         }
 30 | 
 31 |         for function_call in context.function_calls() {
 32 |             if let Expression::Identifier(Identifier { name, .. }) =
 33 |                 function_call.expression.as_ref()
 34 |             {
 35 |                 if name != "require" {
 36 |                     continue;
 37 |                 }
 38 | 
 39 |                 // Now, check for arguments of the `require(..., "message")` function call
 40 |                 let arguments = function_call
 41 |                     .arguments
 42 |                     .clone()
 43 |                     .into_iter()
 44 |                     .map(|n| n.into())
 45 |                     .collect::<Vec<ASTNode>>();
 46 | 
 47 |                 let ast_nodes: &[&ASTNode] = &(arguments.iter().collect::<Vec<_>>());
 48 |                 self.check_eligibility_and_capture(context, ast_nodes, &(function_call.into()))?;
 49 |             }
 50 |         }
 51 | 
 52 |         Ok(!self.found_instances.is_empty())
 53 |     }
 54 | 
 55 |     fn severity(&self) -> IssueSeverity {
 56 |         IssueSeverity::High
 57 |     }
 58 | 
 59 |     fn title(&self) -> String {
 60 |         String::from("Use of `tx.origin` for authentication")
 61 |     }
 62 | 
 63 |     fn description(&self) -> String {
 64 |         String::from(
 65 |             "Using `tx.origin` may lead to problems when users are interacting via smart contract with your \
 66 |             protocol. It is recommended to use `msg.sender` for authentication.",
 67 |         )
 68 |     }
 69 | 
 70 |     fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
 71 |         self.found_instances.clone()
 72 |     }
 73 | 
 74 |     fn name(&self) -> String {
 75 |         format!("{}", IssueDetectorNamePool::TxOriginUsedForAuth)
 76 |     }
 77 | }
 78 | 
 79 | impl TxOriginUsedForAuthDetector {
 80 |     fn check_eligibility_and_capture(
 81 |         &mut self,
 82 |         context: &WorkspaceContext,
 83 |         check_nodes: &[&ASTNode],
 84 |         capture_node: &ASTNode,
 85 |     ) -> Result<(), Box<dyn Error>> {
 86 |         // Boilerplate
 87 |         let callgraphs = CallGraphConsumer::get(context, check_nodes, CallGraphDirection::Inward)?;
 88 |         for callgraph in callgraphs {
 89 |             let mut tracker = MsgSenderAndTxOriginTracker::default();
 90 |             callgraph.accept(context, &mut tracker)?;
 91 | 
 92 |             if tracker.satisfied() {
 93 |                 capture!(self, context, capture_node);
 94 |             }
 95 |         }
 96 |         Ok(())
 97 |     }
 98 | }
 99 | 
100 | #[derive(Default)]
101 | struct MsgSenderAndTxOriginTracker {
102 |     reads_msg_sender: bool,
103 |     reads_tx_origin: bool,
104 | }
105 | 
106 | impl MsgSenderAndTxOriginTracker {
107 |     /// To avoid FP (msg.sender == tx.origin) we require that tx.origin is present and msg.sender is
108 |     /// absent for it to be considered satisfied
109 |     fn satisfied(&self) -> bool {
110 |         self.reads_tx_origin && !self.reads_msg_sender
111 |     }
112 | }
113 | 
114 | impl CallGraphVisitor for MsgSenderAndTxOriginTracker {
115 |     fn visit_any(&mut self, node: &crate::ast::ASTNode) -> eyre::Result<()> {
116 |         let member_accesses = ExtractMemberAccesses::from(node).extracted;
117 | 
118 |         let has_msg_sender = member_accesses.iter().any(|member_access| {
119 |             member_access.member_name == "sender"
120 |                 && if let Expression::Identifier(identifier) = member_access.expression.as_ref() {
121 |                     identifier.name == "msg"
122 |                 } else {
123 |                     false
124 |                 }
125 |         });
126 |         self.reads_msg_sender = self.reads_msg_sender || has_msg_sender;
127 | 
128 |         let has_tx_origin = member_accesses.iter().any(|member_access| {
129 |             member_access.member_name == "origin"
130 |                 && if let Expression::Identifier(identifier) = member_access.expression.as_ref() {
131 |                     identifier.name == "tx"
132 |                 } else {
133 |                     false
134 |                 }
135 |         });
136 |         self.reads_tx_origin = self.reads_tx_origin || has_tx_origin;
137 | 
138 |         Ok(())
139 |     }
140 | }
141 | 
142 | #[cfg(test)]
143 | mod tx_origin_used_for_auth_detector {
144 | 
145 |     use crate::detect::{
146 |         detector::IssueDetector, high::tx_origin_used_for_auth::TxOriginUsedForAuthDetector,
147 |     };
148 | 
149 |     #[test]
150 | 
151 |     fn test_tx_origin_used_for_auth() {
152 |         let context = crate::detect::test_utils::load_solidity_source_unit(
153 |             "../tests/contract-playground/src/TxOriginUsedForAuth.sol",
154 |         );
155 | 
156 |         let mut detector = TxOriginUsedForAuthDetector::default();
157 |         let found = detector.detect(&context).unwrap();
158 |         assert!(found);
159 |         assert_eq!(detector.instances().len(), 3);
160 |     }
161 | }
162 | 
```

--------------------------------------------------------------------------------
/.github/workflows/reports.yml:
--------------------------------------------------------------------------------

```yaml
  1 | on: [push, pull_request, workflow_dispatch]
  2 | 
  3 | name: Reports Workflow
  4 | 
  5 | concurrency:
  6 |   group: ci-${{ github.ref }}-reports
  7 |   cancel-in-progress: true
  8 | 
  9 | jobs:
 10 |   reports-setup:
 11 |     name:  Check Reports 
 12 |     runs-on: ubuntu-latest
 13 |     outputs:
 14 |       rust-nightly: nightly-2025-09-20
 15 |     
 16 |     strategy:
 17 |       fail-fast: false
 18 |       matrix:
 19 |         task:
 20 |           - report-workflow
 21 |           - uniswap_profile-workflow
 22 |           - sablier
 23 |           - adhoc-sol-files-workflow
 24 |           - nft-workflow
 25 |           - nft-workflow-env
 26 |           - ccip-functions-report
 27 |           - hardhat-playground-report
 28 |           - prb-math-report
 29 |           - report-json
 30 |           - adhoc-sol-files-highs-only-json
 31 |           - sarif-report
 32 |           - empty-report
 33 | 
 34 |     steps:
 35 |       - name: Checkout Sources
 36 |         uses: actions/checkout@v4
 37 | 
 38 |       - name: Checkout repository with submodules
 39 |         uses: actions/checkout@v4
 40 |         with:
 41 |           submodules: recursive
 42 | 
 43 |       - name: Cache submodules
 44 |         id: cache-submodules
 45 |         uses: actions/cache@v3
 46 |         with:
 47 |           path: .git/modules
 48 |           key: submodules-${{ runner.os }}-${{ hashFiles('.gitmodules') }}
 49 |           restore-keys: |
 50 |             submodules-${{ runner.os }}-${{ hashFiles('.gitmodules') }}
 51 | 
 52 |       - name: Install Rust Nightly (2025-01-01)
 53 |         uses: actions-rs/toolchain@v1
 54 |         with:
 55 |           profile: minimal
 56 |           toolchain: nightly-2025-09-20
 57 |           override: true
 58 | 
 59 |       - name: Setup Node.js
 60 |         uses: actions/setup-node@v4
 61 |         with:
 62 |           node-version: 20
 63 |           cache: 'npm'
 64 | 
 65 |       - name: Install pnpm
 66 |         uses: pnpm/action-setup@v4
 67 |         with:
 68 |           version: 8
 69 | 
 70 |       - name: Restore Rust Cache
 71 |         uses: Swatinem/rust-cache@v2
 72 | 
 73 |       - name: Prebuild (${{ matrix.task }})
 74 |         run: |
 75 |           cargo build
 76 | 
 77 |       - name: Generate Report (${{ matrix.task }})
 78 |         run: |
 79 |           case "${{ matrix.task }}" in
 80 |             report-workflow)
 81 |               cargo run -- -o ./reports/report-workflow.md  --src src/ ./tests/contract-playground/ --skip-update-check
 82 |               diff ./reports/report.md ./reports/report-workflow.md
 83 |               ;;
 84 |             uniswap_profile-workflow)
 85 |               FOUNDRY_PROFILE=uniswap cargo run -- -o ./reports/uniswap_profile-workflow.md ./tests/contract-playground/ --skip-update-check
 86 |               diff reports/uniswap_profile.md reports/uniswap_profile-workflow.md
 87 |               ;;
 88 |             sablier)
 89 |               pnpm install --prefix tests/2024-05-Sablier/v2-core
 90 |               FOUNDRY_PROFILE=uniswap cargo run -- -o ./reports/sablier.md ./tests/2024-05-Sablier --skip-update-check
 91 |               diff reports/sablier-aderyn-toml-nested-root.md reports/sablier.md
 92 |               ;;
 93 |             adhoc-sol-files-workflow)
 94 |               cargo run -- -o ./reports/adhoc-sol-files-report-workflow.md ./tests/adhoc-sol-files --skip-update-check
 95 |               diff ./reports/adhoc-sol-files-report.md ./reports/adhoc-sol-files-report-workflow.md
 96 |               ;;
 97 |             nft-workflow)
 98 |               cargo run -- -o ./reports/nft-workflow-report.md --src src/ ./tests/foundry-nft-f23 --skip-update-check
 99 |               diff ./reports/nft-report.md ./reports/nft-workflow-report.md
100 |               ;;
101 |             nft-workflow-env)
102 |               cargo run -- -o ./reports/nft-workflow-report-icm.md ./tests/foundry-nft-f23-icm --skip-update-check
103 |               diff ./reports/nft-report-icm.md ./reports/nft-workflow-report-icm.md
104 |               ;;
105 |             ccip-functions-report)
106 |               cargo run -- -o reports/ccip-functions-report-workflow.md tests/ccip-contracts/contracts --src src/v0.8/functions/ -x "tests/,test/,mocks/"  --skip-update-check
107 |               diff ./reports/ccip-functions-report.md ./reports/ccip-functions-report-workflow.md
108 |               ;;
109 |             hardhat-playground-report)
110 |               cargo run -- tests/hardhat-js-playground -o reports/hardhat-playground-report-workflow.md --skip-update-check
111 |               diff ./reports/hardhat-playground-report.md ./reports/hardhat-playground-report-workflow.md
112 |               ;;
113 |             prb-math-report)
114 |               pnpm install --prefix tests/prb-math/
115 |               cargo run -- ./tests/prb-math -o ./reports/prb-math-report-workflow.md --skip-update-check
116 |               diff ./reports/prb-math-report.md ./reports/prb-math-report-workflow.md
117 |               ;;
118 |             report-json)
119 |               cargo run -- -o ./reports/report-workflow.json -i src/ -x lib/ ./tests/contract-playground/ --skip-update-check
120 |               diff ./reports/report.json ./reports/report-workflow.json
121 |               ;;
122 |             adhoc-sol-files-highs-only-json)
123 |               cargo run -- -o ./reports/adhoc-sol-files-highs-only-report-workflow.json ./tests/adhoc-sol-files --skip-update-check --highs-only
124 |               diff ./reports/adhoc-sol-files-highs-only-report.json ./reports/adhoc-sol-files-highs-only-report-workflow.json
125 |               ;;
126 |             sarif-report)
127 |               cargo run -- -o ./reports/ci-report.sarif ./tests/contract-playground/ --skip-update-check
128 |               diff ./reports/report.sarif ./reports/ci-report.sarif
129 |               ;;
130 |             empty-report)
131 |               cargo run -- tests/contract-playground -o reports/empty_report_workflow.md -i IgnoreEverything.sol
132 |               diff ./reports/empty_report.md ./reports/empty_report_workflow.md
133 |               ;;
134 |           esac
135 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/ast/impls/disp/expressions.rs:
--------------------------------------------------------------------------------

```rust
  1 | use crate::ast::*;
  2 | use std::fmt::{Display, Write};
  3 | 
  4 | impl Display for Expression {
  5 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  6 |         match self {
  7 |             Expression::Literal(expr) => expr.fmt(f)?,
  8 |             Expression::Identifier(expr) => expr.fmt(f)?,
  9 |             Expression::UnaryOperation(expr) => expr.fmt(f)?,
 10 |             Expression::BinaryOperation(expr) => expr.fmt(f)?,
 11 |             Expression::Conditional(expr) => expr.fmt(f)?,
 12 |             Expression::Assignment(expr) => expr.fmt(f)?,
 13 |             Expression::FunctionCall(expr) => expr.fmt(f)?,
 14 |             Expression::FunctionCallOptions(expr) => expr.fmt(f)?,
 15 |             Expression::IndexAccess(expr) => expr.fmt(f)?,
 16 |             Expression::IndexRangeAccess(expr) => expr.fmt(f)?,
 17 |             Expression::MemberAccess(expr) => expr.fmt(f)?,
 18 |             Expression::ElementaryTypeNameExpression(expr) => expr.fmt(f)?,
 19 |             Expression::TupleExpression(expr) => expr.fmt(f)?,
 20 |             Expression::NewExpression(expr) => expr.fmt(f)?,
 21 |         }
 22 | 
 23 |         Ok(())
 24 |     }
 25 | }
 26 | 
 27 | impl Display for UnaryOperation {
 28 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 29 |         f.write_fmt(format_args!("{}{}", self.sub_expression, self.operator.as_str()))
 30 |     }
 31 | }
 32 | 
 33 | impl Display for BinaryOperation {
 34 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 35 |         f.write_fmt(format_args!(
 36 |             "{} {} {}",
 37 |             self.left_expression, self.operator, self.right_expression
 38 |         ))
 39 |     }
 40 | }
 41 | 
 42 | impl Display for Conditional {
 43 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 44 |         f.write_fmt(format_args!(
 45 |             "{} ? {} : {}",
 46 |             self.condition, self.true_expression, self.false_expression
 47 |         ))
 48 |     }
 49 | }
 50 | 
 51 | impl Display for Assignment {
 52 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 53 |         f.write_fmt(format_args!(
 54 |             "{} {} {}",
 55 |             self.left_hand_side, self.operator, self.right_hand_side
 56 |         ))
 57 |     }
 58 | }
 59 | 
 60 | impl Display for FunctionCall {
 61 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 62 |         f.write_fmt(format_args!("{}", self.expression))?;
 63 |         f.write_str("(")?;
 64 | 
 65 |         for (i, argument) in self.arguments.iter().enumerate() {
 66 |             if i > 0 {
 67 |                 f.write_str(", ")?;
 68 |             }
 69 | 
 70 |             f.write_fmt(format_args!("{argument}"))?;
 71 |         }
 72 | 
 73 |         f.write_str(")")
 74 |     }
 75 | }
 76 | 
 77 | impl Display for FunctionCallOptions {
 78 |     #[allow(clippy::print_in_format_impl)]
 79 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 80 |         let option_count = self.options.len();
 81 | 
 82 |         if self.names.len() != option_count {
 83 |             eprintln!("ERROR: invalid FunctionCallOptions: {:?}, {:?}", self.names, self.options);
 84 | 
 85 |             return Err(std::fmt::Error);
 86 |         }
 87 | 
 88 |         f.write_fmt(format_args!("{}", self.expression))?;
 89 | 
 90 |         f.write_char('{')?;
 91 | 
 92 |         for i in 0..option_count {
 93 |             if i > 0 {
 94 |                 f.write_str(", ")?;
 95 |             }
 96 | 
 97 |             f.write_fmt(format_args!("{}: {}", self.names[i], self.options[i]))?;
 98 |         }
 99 | 
100 |         f.write_char('}')?;
101 | 
102 |         if let Some(arguments) = self.arguments.as_ref() {
103 |             f.write_char('(')?;
104 | 
105 |             for (i, argument) in arguments.iter().enumerate() {
106 |                 if i > 0 {
107 |                     f.write_str(", ")?;
108 |                 }
109 | 
110 |                 f.write_fmt(format_args!("{argument}"))?;
111 |             }
112 | 
113 |             f.write_char(')')?;
114 |         }
115 | 
116 |         Ok(())
117 |     }
118 | }
119 | 
120 | impl Display for IndexAccess {
121 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 |         if let Some(index_expression) = &self.index_expression {
123 |             f.write_fmt(format_args!("{}[{}]", self.base_expression, index_expression))
124 |         } else {
125 |             f.write_fmt(format_args!("{}[]", self.base_expression))
126 |         }
127 |     }
128 | }
129 | 
130 | impl Display for IndexRangeAccess {
131 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 |         f.write_fmt(format_args!("{}[", self.base_expression))?;
133 | 
134 |         if let Some(start_expression) = self.start_expression.as_ref() {
135 |             f.write_fmt(format_args!("{start_expression}"))?;
136 |         }
137 | 
138 |         f.write_str(":")?;
139 | 
140 |         if let Some(end_expression) = self.end_expression.as_ref() {
141 |             f.write_fmt(format_args!("{end_expression}"))?;
142 |         }
143 | 
144 |         f.write_str("]")
145 |     }
146 | }
147 | 
148 | impl Display for MemberAccess {
149 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 |         f.write_fmt(format_args!("{}.{}", self.expression, self.member_name))
151 |     }
152 | }
153 | 
154 | impl Display for ElementaryTypeNameExpression {
155 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 |         f.write_fmt(format_args!("{}", self.type_name))
157 |     }
158 | }
159 | 
160 | impl Display for TupleExpression {
161 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 |         f.write_str("(")?;
163 | 
164 |         for (i, component) in self.components.iter().enumerate() {
165 |             if i > 0 {
166 |                 f.write_str(", ")?;
167 |             }
168 | 
169 |             if let Some(component) = component {
170 |                 f.write_fmt(format_args!("{component}"))?;
171 |             }
172 |         }
173 | 
174 |         f.write_str(")")
175 |     }
176 | }
177 | 
178 | impl Display for NewExpression {
179 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 |         f.write_fmt(format_args!("new {}", self.type_name))
181 |     }
182 | }
183 | 
```

--------------------------------------------------------------------------------
/aderyn/src/lib.rs:
--------------------------------------------------------------------------------

```rust
  1 | use aderyn_driver::detector::{IssueSeverity, get_all_detectors_names, get_issue_detector_by_name};
  2 | use semver::Version;
  3 | use serde_json::Value;
  4 | use std::{cmp::Ordering, fs::File, io::Write, path::PathBuf, str::FromStr};
  5 | use strum::IntoEnumIterator;
  6 | 
  7 | pub mod birdsong;
  8 | pub mod completions;
  9 | pub mod lsp;
 10 | pub mod mcp;
 11 | 
 12 | mod panic;
 13 | 
 14 | pub fn create_aderyn_toml_file_at(directory: String) {
 15 |     let solidity_dir = find_solidity_dir(&directory);
 16 |     let aderyn_toml_path = PathBuf::from_str(&directory).unwrap().join("aderyn.toml");
 17 |     let mut file = File::create_new(aderyn_toml_path.clone()).expect("File already exists!");
 18 |     file.write_fmt(format_args!(
 19 |         include_str!("../templates/aderyn.toml"),
 20 |         format!("\"{}\"", &solidity_dir)
 21 |     ))
 22 |     .expect("unable to write to aderyn.toml");
 23 |     println!("Created aderyn.toml at {}", aderyn_toml_path.display());
 24 | }
 25 | 
 26 | pub fn find_solidity_dir(root: &str) -> String {
 27 |     let path = PathBuf::from_str(root).expect("invalid path root");
 28 |     let indicators = ["hardhat.config.ts", "hardhat.config.js", "foundry.toml", "soldeer.toml"];
 29 | 
 30 |     // Check for indicators in the same directory level
 31 |     for indicator in indicators {
 32 |         let target = path.join(indicator);
 33 |         if target.is_file() {
 34 |             return path.to_string_lossy().to_string();
 35 |         }
 36 |     }
 37 | 
 38 |     // Check for indicators one level below
 39 |     for indicator in indicators {
 40 |         let mut nodes = std::fs::read_dir(path.clone())
 41 |             .expect("reading path failed")
 42 |             .flatten()
 43 |             .collect::<Vec<_>>();
 44 |         nodes.sort_by(|a, _| {
 45 |             if a.file_name().to_string_lossy().contains("contract") {
 46 |                 Ordering::Less
 47 |             } else {
 48 |                 Ordering::Greater
 49 |             }
 50 |         });
 51 |         for node in nodes {
 52 |             if !node.path().is_dir() {
 53 |                 continue;
 54 |             }
 55 |             let target = node.path().join(indicator);
 56 |             if target.is_file() {
 57 |                 let location = node.path();
 58 |                 let toml_entry = location.strip_prefix(path).expect("stripping failed");
 59 |                 return toml_entry.to_string_lossy().to_string();
 60 |             }
 61 |         }
 62 |     }
 63 | 
 64 |     root.to_string()
 65 | }
 66 | 
 67 | pub fn initialize_niceties() {
 68 |     // Crash with a nice message on panic
 69 |     panic::add_handler();
 70 | 
 71 |     // Logger
 72 |     #[cfg(debug_assertions)]
 73 |     {
 74 |         simplelog::WriteLogger::init(
 75 |             simplelog::LevelFilter::Info,
 76 |             simplelog::Config::default(),
 77 |             File::create(concat!(env!("CARGO_MANIFEST_DIR"), "/../app.log")).unwrap(),
 78 |         )
 79 |         .unwrap();
 80 |     }
 81 | }
 82 | 
 83 | pub fn print_detail_view(detector_name: &str) {
 84 |     let all_detector_names = get_all_detectors_names();
 85 |     if !all_detector_names.contains(&detector_name.to_string()) {
 86 |         println!("Couldn't recognize detector with name {}", detector_name);
 87 |         return;
 88 |     }
 89 |     let detector = get_issue_detector_by_name(detector_name);
 90 |     println!("\nDetector {}", detector_name);
 91 |     println!();
 92 |     println!("Title");
 93 |     println!("{}", detector.title());
 94 |     println!();
 95 |     println!("Severity");
 96 |     println!("{}", detector.severity());
 97 |     println!();
 98 |     println!("Description");
 99 |     println!("{}", detector.description());
100 |     println!();
101 | }
102 | 
103 | pub fn print_all_detectors_view() {
104 |     let all_detector_names = get_all_detectors_names();
105 |     println!("\nDetector Registry");
106 |     println!();
107 |     println!("{}   Title (Rating)", right_pad("Name", 30));
108 |     println!();
109 |     for severity in IssueSeverity::iter() {
110 |         print_detectors_view_with_severity(severity, &all_detector_names);
111 |         println!();
112 |     }
113 |     println!();
114 | }
115 | 
116 | pub fn print_detectors_view_with_severity(severity: IssueSeverity, detectors_names: &[String]) {
117 |     let concerned_detectors = detectors_names
118 |         .iter()
119 |         .filter(|name| {
120 |             let detector = get_issue_detector_by_name(name);
121 |             detector.severity() == severity
122 |         })
123 |         .collect::<Vec<_>>();
124 | 
125 |     if concerned_detectors.is_empty() {
126 |         return;
127 |     }
128 | 
129 |     println!("{}\n", severity);
130 |     for name in concerned_detectors {
131 |         let detector = get_issue_detector_by_name(name);
132 |         println!("{} - {}", right_pad(name, 30), detector.title(),);
133 |     }
134 |     println!();
135 | }
136 | 
137 | fn right_pad(s: &str, by: usize) -> String {
138 |     if s.len() > by {
139 |         return s.to_string();
140 |     }
141 |     let extra_spaces = by - s.len();
142 |     let spaces = " ".repeat(extra_spaces);
143 |     let mut new_string = s.to_string();
144 |     new_string.push_str(&spaces);
145 |     new_string
146 | }
147 | 
148 | pub static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
149 | 
150 | pub fn aderyn_is_currently_running_newest_version() -> Option<bool> {
151 |     let client = reqwest::blocking::Client::builder()
152 |         .user_agent(APP_USER_AGENT)
153 |         .build()
154 |         .expect("client is unable to initialize");
155 | 
156 |     let latest_version_checker =
157 |         client.get("https://api.github.com/repos/Cyfrin/aderyn/releases/latest").send().ok()?;
158 | 
159 |     let data = latest_version_checker.json::<Value>().ok()?;
160 |     let version_string = data.get("tag_name")?.as_str()?;
161 |     let newest = Version::parse(version_string.replace("aderyn-v", "").as_str()).ok()?;
162 |     let current = Version::parse(env!("CARGO_PKG_VERSION")).expect("Pkg version not available");
163 | 
164 |     Some(current >= newest)
165 | }
166 | 
167 | #[cfg(test)]
168 | mod latest_version_checker_tests {
169 |     use super::*;
170 | 
171 |     #[test]
172 |     #[ignore = "fails when frequently run as github will rate limit"]
173 |     fn can_get_latest_version_from_github_releases() {
174 |         assert!(aderyn_is_currently_running_newest_version().is_some())
175 |     }
176 | }
177 | 
```

--------------------------------------------------------------------------------
/benchmarks/non-reentrant-before-others/report/both/regression.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="960" height="540" viewBox="0 0 960 540" xmlns="http://www.w3.org/2000/svg">
 2 | <text x="480" y="32" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="16.129032258064516" opacity="1" fill="#000000">
 3 | non-reentrant-before-others
 4 | </text>
 5 | <text x="27" y="263" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000" transform="rotate(270, 27, 263)">
 6 | Total sample time (ms)
 7 | </text>
 8 | <text x="510" y="513" dy="-0.5ex" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
 9 | Iterations (x 10^3)
10 | </text>
11 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="87" y2="53"/>
12 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="237" y1="472" x2="237" y2="53"/>
13 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="388" y1="472" x2="388" y2="53"/>
14 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="539" y1="472" x2="539" y2="53"/>
15 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="690" y1="472" x2="690" y2="53"/>
16 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="841" y1="472" x2="841" y2="53"/>
17 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="472" x2="932" y2="472"/>
18 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="399" x2="932" y2="399"/>
19 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="326" x2="932" y2="326"/>
20 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="253" x2="932" y2="253"/>
21 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="180" x2="932" y2="180"/>
22 | <line opacity="0.2" stroke="#000000" stroke-width="1" x1="87" y1="107" x2="932" y2="107"/>
23 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="86,53 86,472 "/>
24 | <text x="77" y="472" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
25 | 0.0
26 | </text>
27 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,472 86,472 "/>
28 | <text x="77" y="399" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
29 | 20.0
30 | </text>
31 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,399 86,399 "/>
32 | <text x="77" y="326" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
33 | 40.0
34 | </text>
35 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,326 86,326 "/>
36 | <text x="77" y="253" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
37 | 60.0
38 | </text>
39 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,253 86,253 "/>
40 | <text x="77" y="180" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
41 | 80.0
42 | </text>
43 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,180 86,180 "/>
44 | <text x="77" y="107" dy="0.5ex" text-anchor="end" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
45 | 100.0
46 | </text>
47 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="81,107 86,107 "/>
48 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 932,473 "/>
49 | <text x="87" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
50 | 0
51 | </text>
52 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="87,473 87,478 "/>
53 | <text x="237" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
54 | 5
55 | </text>
56 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="237,473 237,478 "/>
57 | <text x="388" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
58 | 10
59 | </text>
60 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="388,473 388,478 "/>
61 | <text x="539" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
62 | 15
63 | </text>
64 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="539,473 539,478 "/>
65 | <text x="690" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
66 | 20
67 | </text>
68 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="690,473 690,478 "/>
69 | <text x="841" y="483" dy="0.76em" text-anchor="middle" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
70 | 25
71 | </text>
72 | <polyline fill="none" opacity="1" stroke="#000000" stroke-width="1" points="841,473 841,478 "/>
73 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="1" points="87,472 932,53 "/>
74 | <polygon opacity="0.25" fill="#E31A1C" points="87,472 932,63 932,53 "/>
75 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="1" points="87,472 932,140 "/>
76 | <polygon opacity="0.25" fill="#1F78B4" points="87,472 932,142 932,136 "/>
77 | <text x="132" y="68" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
78 | Base Sample
79 | </text>
80 | <text x="132" y="83" dy="0.76em" text-anchor="start" font-family="sans-serif" font-size="9.67741935483871" opacity="1" fill="#000000">
81 | New Sample
82 | </text>
83 | <polyline fill="none" opacity="1" stroke="#E31A1C" stroke-width="2" points="102,73 122,73 "/>
84 | <polyline fill="none" opacity="1" stroke="#1F78B4" stroke-width="2" points="102,88 122,88 "/>
85 | </svg>
86 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/detect/low/literal_instead_of_constant.rs:
--------------------------------------------------------------------------------

```rust
  1 | use std::{
  2 |     collections::{BTreeMap, HashMap},
  3 |     error::Error,
  4 | };
  5 | 
  6 | use crate::{
  7 |     ast::{Literal, LiteralKind, NodeID},
  8 |     capture,
  9 |     context::{
 10 |         browser::{
 11 |             ExtractFunctionDefinitions, ExtractLiterals, ExtractModifierDefinitions,
 12 |             GetImmediateParent,
 13 |         },
 14 |         workspace::{ASTNode, WorkspaceContext},
 15 |     },
 16 |     detect::detector::{IssueDetector, IssueDetectorNamePool, IssueSeverity},
 17 | };
 18 | use eyre::Result;
 19 | 
 20 | #[derive(Default)]
 21 | pub struct LiteralsInsteadOfConstantsDetector {
 22 |     // Keys are: [0] source file name, [1] line number, [2] character location of node.
 23 |     // Do not add items manually, use `capture!` to add nodes to this BTreeMap.
 24 |     found_instances: BTreeMap<(String, usize, String), NodeID>,
 25 | }
 26 | 
 27 | impl IssueDetector for LiteralsInsteadOfConstantsDetector {
 28 |     fn detect(&mut self, context: &WorkspaceContext) -> Result<bool, Box<dyn Error>> {
 29 |         // Get all contracts
 30 |         // For each contract
 31 |         //      Get all Function definitions (and to the same for modifiers)
 32 |         //          Get all literals
 33 |         //          For each literal
 34 |         //              if literal.value is not 0 or 1
 35 |         //                  if the literal.value appears more than once, then capture all instances
 36 | 
 37 |         for contract in context.contract_definitions() {
 38 |             let mut literal_values_found: HashMap<String, Vec<Literal>> = HashMap::new();
 39 | 
 40 |             for function in ExtractFunctionDefinitions::from(contract).extracted.into_iter() {
 41 |                 for literal in ExtractLiterals::from(&function).extracted.into_iter() {
 42 |                     if (literal.kind == LiteralKind::Number
 43 |                         && literal.value != Some(String::from("0"))
 44 |                         && literal.value != Some(String::from("1")))
 45 |                         && literal.value != Some(String::from("2"))
 46 |                         || literal.kind == LiteralKind::HexString
 47 |                         || literal.kind == LiteralKind::Address
 48 |                     {
 49 |                         // If the literal is used as an index access in a variable, don't capture it
 50 |                         if let Some(ASTNode::IndexAccess(_)) = literal.parent(context) {
 51 |                             continue;
 52 |                         }
 53 | 
 54 |                         if let Some(literal_value) = literal.value.as_ref() {
 55 |                             if literal_values_found.contains_key(literal_value) {
 56 |                                 literal_values_found.get_mut(literal_value).unwrap().push(literal);
 57 |                             } else {
 58 |                                 literal_values_found.insert(literal_value.clone(), vec![literal]);
 59 |                             }
 60 |                         }
 61 |                     }
 62 |                 }
 63 |             }
 64 | 
 65 |             for modifier in ExtractModifierDefinitions::from(contract).extracted.into_iter() {
 66 |                 for literal in ExtractLiterals::from(&modifier).extracted.into_iter() {
 67 |                     if (literal.kind == LiteralKind::Number
 68 |                         && literal.value != Some(String::from("0"))
 69 |                         && literal.value != Some(String::from("1")))
 70 |                         && literal.value != Some(String::from("2"))
 71 |                         || literal.kind == LiteralKind::HexString
 72 |                         || literal.kind == LiteralKind::Address
 73 |                     {
 74 |                         // If the literal is used as an index access in a variable, don't capture it
 75 |                         if let Some(ASTNode::IndexAccess(_)) = context.get_parent(literal.id) {
 76 |                             continue;
 77 |                         }
 78 | 
 79 |                         if let Some(literal_value) = literal.value.as_ref() {
 80 |                             if literal_values_found.contains_key(literal_value) {
 81 |                                 literal_values_found.get_mut(literal_value).unwrap().push(literal);
 82 |                             } else {
 83 |                                 literal_values_found.insert(literal_value.clone(), vec![literal]);
 84 |                             }
 85 |                         }
 86 |                     }
 87 |                 }
 88 |             }
 89 | 
 90 |             for (_, literals) in literal_values_found.iter() {
 91 |                 if literals.len() > 1 {
 92 |                     for literal in literals {
 93 |                         capture!(self, context, literal);
 94 |                     }
 95 |                 }
 96 |             }
 97 |         }
 98 | 
 99 |         Ok(!self.found_instances.is_empty())
100 |     }
101 | 
102 |     fn title(&self) -> String {
103 |         String::from("Literal Instead of Constant")
104 |     }
105 | 
106 |     fn description(&self) -> String {
107 |         String::from(
108 |             "Define and use `constant` variables instead of using literals. If the same constant literal value is used multiple times, create a constant state variable and reference it throughout the contract.",
109 |         )
110 |     }
111 | 
112 |     fn severity(&self) -> IssueSeverity {
113 |         IssueSeverity::Low
114 |     }
115 | 
116 |     fn instances(&self) -> BTreeMap<(String, usize, String), NodeID> {
117 |         self.found_instances.clone()
118 |     }
119 | 
120 |     fn name(&self) -> String {
121 |         format!("{}", IssueDetectorNamePool::LiteralInsteadOfConstant)
122 |     }
123 | }
124 | 
125 | #[cfg(test)]
126 | mod constants_instead_of_literals_tests {
127 | 
128 |     use super::LiteralsInsteadOfConstantsDetector;
129 |     use crate::detect::detector::IssueDetector;
130 | 
131 |     #[test]
132 | 
133 |     fn test_constants_instead_of_literals_by_loading_contract_directly() {
134 |         let context = crate::detect::test_utils::load_solidity_source_unit(
135 |             "../tests/contract-playground/src/ConstantsLiterals.sol",
136 |         );
137 | 
138 |         let mut detector = LiteralsInsteadOfConstantsDetector::default();
139 |         let found = detector.detect(&context).unwrap();
140 |         assert!(found);
141 |         assert_eq!(detector.instances().len(), 8);
142 |     }
143 | }
144 | 
```

--------------------------------------------------------------------------------
/aderyn_core/src/context/router/external_calls.rs:
--------------------------------------------------------------------------------

```rust
  1 | use super::{ECDest, Router};
  2 | use crate::{
  3 |     ast::{
  4 |         ASTNode, ContractDefinition, FunctionCall, FunctionDefinition, FunctionKind, Visibility,
  5 |     },
  6 |     context::workspace::WorkspaceContext,
  7 | };
  8 | use std::collections::{HashMap, hash_map::Entry};
  9 | 
 10 | impl Router {
 11 |     /// Given a function call, resolve the function definition with it's selector.
 12 |     ///
 13 |     /// If no function is found with the said selector, it tries to retrieve a fallback function.
 14 |     /// Since function selector is data that's being passed it cannot point to receive function.
 15 |     ///
 16 |     /// Suspect here could be a variable as well
 17 |     pub(super) fn _resolve_external_call<'a>(
 18 |         &self,
 19 |         context: &'a WorkspaceContext,
 20 |         base_contract: &'a ContractDefinition,
 21 |         func_call: &'a FunctionCall,
 22 |     ) -> Option<ECDest> {
 23 |         // do not resolve if it's internal function call
 24 |         if func_call.is_internal_call() == Some(true) {
 25 |             return None;
 26 |         }
 27 | 
 28 |         // works for both public variables and functions
 29 |         let selector = func_call.suspected_function_selector(context)?;
 30 |         self._resolve_function_selector(base_contract, selector)
 31 |     }
 32 | 
 33 |     pub(super) fn _resolve_fallback_function<'a>(
 34 |         &self,
 35 |         context: &'a WorkspaceContext,
 36 |         base_contract: &'a ContractDefinition,
 37 |     ) -> Option<&'a FunctionDefinition> {
 38 |         // check if it's illegal base contract type
 39 |         if !base_contract.is_deployable_contract() {
 40 |             return None;
 41 |         }
 42 |         let lookup_index = self.external_calls.get(&base_contract.id)?;
 43 |         if let Some(ECDest::Fallback(func_id)) = lookup_index.routes.get("FALLBACK")
 44 |             && let Some(ASTNode::FunctionDefinition(fallback)) = context.nodes.get(func_id)
 45 |         {
 46 |             return Some(fallback);
 47 |         }
 48 |         None
 49 |     }
 50 | 
 51 |     pub(super) fn _resolve_receive_function<'a>(
 52 |         &self,
 53 |         context: &'a WorkspaceContext,
 54 |         base_contract: &'a ContractDefinition,
 55 |     ) -> Option<&'a FunctionDefinition> {
 56 |         // check if it's illegal base contract type
 57 |         if !base_contract.is_deployable_contract() {
 58 |             return None;
 59 |         }
 60 |         let lookup_index = self.external_calls.get(&base_contract.id)?;
 61 |         if let Some(ECDest::Receive(func_id)) = lookup_index.routes.get("RECEIVE")
 62 |             && let Some(ASTNode::FunctionDefinition(fallback)) = context.nodes.get(func_id)
 63 |         {
 64 |             return Some(fallback);
 65 |         }
 66 |         None
 67 |     }
 68 | 
 69 |     pub(super) fn _resolve_function_selector(
 70 |         &self,
 71 |         base_contract: &ContractDefinition,
 72 |         selector: impl AsRef<str>,
 73 |     ) -> Option<ECDest> {
 74 |         // check if it's illegal base contract type
 75 |         if !base_contract.is_deployable_contract() {
 76 |             return None;
 77 |         }
 78 | 
 79 |         let lookup_index = self.external_calls.get(&base_contract.id)?;
 80 | 
 81 |         match lookup_index.routes.get(selector.as_ref()) {
 82 |             Some(resolved) => Some(resolved.clone()),
 83 |             None => lookup_index.routes.get("FALLBACK").cloned(),
 84 |         }
 85 |     }
 86 | }
 87 | 
 88 | /// If function selector field isn't present, this algorithm cannot work.
 89 | /// Therefore, if for some reason it's not found, we return an empty hashmap
 90 | pub(super) fn build_ec_router_for_contract(
 91 |     context: &WorkspaceContext,
 92 |     base_contract: &ContractDefinition,
 93 | ) -> HashMap<String, ECDest> {
 94 |     let c3 = base_contract.c3(context).collect::<Vec<_>>();
 95 |     let mut routes = HashMap::new();
 96 |     for contract in c3.iter() {
 97 |         // Loop through public state variables
 98 |         for var in contract.top_level_variables() {
 99 |             if var.visibility == Visibility::Public {
100 |                 let Some(func_selector) = var.function_selector.as_ref() else {
101 |                     return HashMap::new();
102 |                 };
103 |                 if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
104 |                     e.insert(ECDest::PseudoExtFn(var.id));
105 |                 }
106 |             }
107 |         }
108 |         // Loop through externally available functions
109 |         for func in contract.function_definitions() {
110 |             match *func.kind() {
111 |                 FunctionKind::Function => {
112 |                     match func.visibility {
113 |                         Visibility::Public => {
114 |                             let Some(func_selector) = func.function_selector.as_ref() else {
115 |                                 return HashMap::new();
116 |                             };
117 |                             if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
118 |                                 e.insert(ECDest::PublicFn(func.id));
119 |                             }
120 |                         }
121 |                         Visibility::External => {
122 |                             let Some(func_selector) = func.function_selector.as_ref() else {
123 |                                 return HashMap::new();
124 |                             };
125 |                             if let Entry::Vacant(e) = routes.entry(func_selector.to_string()) {
126 |                                 e.insert(ECDest::RealExtFn(func.id));
127 |                             }
128 |                         }
129 |                         _ => {}
130 |                     };
131 |                 }
132 |                 FunctionKind::Receive => {
133 |                     if let Entry::Vacant(e) = routes.entry("RECEIVE".to_string()) {
134 |                         e.insert(ECDest::Receive(func.id));
135 |                     }
136 |                 }
137 |                 FunctionKind::Fallback => {
138 |                     if let Entry::Vacant(e) = routes.entry("FALLBACK".to_string()) {
139 |                         e.insert(ECDest::Fallback(func.id));
140 |                     }
141 |                 }
142 |                 FunctionKind::FreeFunction => unreachable!(), // can't be inside a contract.
143 |                 FunctionKind::Constructor => {}
144 |             };
145 |         }
146 |     }
147 |     routes
148 | }
149 | 
```
Page 10/103FirstPrevNextLast