Skip to content

Fix struct type resolution and SSA resilience for complex codebases#2962

Open
nisedo wants to merge 2 commits intomasterfrom
fix/struct-forward-reference-type-resolution
Open

Fix struct type resolution and SSA resilience for complex codebases#2962
nisedo wants to merge 2 commits intomasterfrom
fix/struct-forward-reference-type-resolution

Conversation

@nisedo
Copy link
Contributor

@nisedo nisedo commented Feb 8, 2026

Summary

Fixes multiple crashes when analyzing complex codebases with contract name collisions, deeply inherited state variables, and ternary operators in partially-parsed functions.

Bug 1: Struct Type Resolution with Same-Name Contract Collisions

Root Cause: FileScope.contracts is a dict[str, Contract] keyed by contract name. When a file imports a contract with the same name as a local contract (via import { Structs as Alias }), the imported contract silently overwrites the local one. The parse_type() function in type_parsing.py builds all_structures and all_enums from scope.contracts.values(), which loses all structs/enums from the overwritten contract.

Fix: Changed all_structures and all_enums to be built from sl.contracts (the full compilation unit) instead of scope.contracts.values(). This matches the existing approach already used for top-level contexts and avoids the dict collision entirely.

Files: slither/solc_parsing/solidity_types/type_parsing.py

Bug 2: SSA Assertion Failure on Untracked State Variables

Root Cause: The get() function in ssa.py checks isinstance(variable, StateVariable) and variable.canonical_name in state_variables_instances, but if a ReferenceVariable.points_to refers to a state variable not in the current contract's SSA instances (e.g., from an external contract or unresolved due to name collisions), the StateVariable falls through all handlers and hits an assertion at line 632 where StateVariable is not listed as an allowed type.

Fix: When a StateVariable is not in state_variables_instances, return it as-is (same treatment as Constant, Contract, etc.). Also hardened all downstream SSA consumers:

  • update_lvalue(): Skip updates for unknown state variables
  • last_name(): Filter to SSA-typed variables, return None when no candidates found
  • fix_phi_rvalues_and_storage_ref(): Filter None from phi rvalues
  • fix_phi() in Contract: Skip unknown state variable names
  • convert_variable_to_non_ssa(): Accept raw StateVariable/LocalVariable

Files: slither/slithir/utils/ssa.py, slither/core/declarations/contract.py, slither/analyses/data_dependency/data_dependency.py

Bug 3: One Parsing Failure Aborts All Sibling Functions

Root Cause: analyze_content_functions() and analyze_content_modifiers() wrapped their entire loop in a single try/except. When one function raised VariableNotFound (e.g., an unresolvable struct constructor), ALL remaining functions in the contract were skipped — including their ternary rewriting phase. This caused surviving ternary expressions to crash during IR generation.

Fix: Moved try/except inside the loop so each function/modifier is independently analyzed. Also added per-node error handling in expression analysis within analyze_content() so that a failure in one CFG node doesn't prevent ternary rewriting from running on other nodes.

Files: slither/solc_parsing/declarations/contract.py, slither/solc_parsing/declarations/function.py

Test Results

  • SSA generation unit tests: 14 passed, 12 xfailed
  • AST parsing e2e tests: 6593 passed
  • Detector e2e tests: 381 passed
  • All other unit tests: 247 passed, 14 xfailed
  • Total: 7221 tests passed, 0 regressions
  • Successfully analyzed a real-world 806-contract codebase that previously crashed

🤖 Generated with Claude Code

When multiple contracts share the same name (e.g. both files define
`abstract contract Structs`), `scope.contracts` dict overwrites one
with the other, causing struct lookups via `all_structures` to fail
with `ParsingError: Type not found struct ContractName.StructName`.

Use `sl.contracts` (full compilation unit) instead of
`scope.contracts.values()` to build `all_structures` and `all_enums`,
matching the existing approach used for top-level contexts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nisedo nisedo requested a review from smonicas as a code owner February 8, 2026 11:07
Three classes of issues fixed:

1. SSA handles state variables not in current contract's instances:
   - get() returns raw StateVariable instead of assertion failure
   - update_lvalue() skips updates for unknown state variables
   - last_name() filters to SSA-typed variables, returns None safely
   - fix_phi_rvalues_and_storage_ref() filters None from rvalues
   - fix_phi() in Contract skips unknown state variable names
   - convert_variable_to_non_ssa() accepts raw StateVariable/LocalVariable

2. Per-item error handling prevents one failure from aborting siblings:
   - analyze_content_functions() catches errors per-function, not per-loop
   - analyze_content_modifiers() catches errors per-modifier, not per-loop
   - Expression analysis in analyze_content() catches errors per-node

3. Null safety in ternary rewriting:
   - Skip nodes with None expressions in _rewrite_ternary_as_if_else()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nisedo nisedo changed the title Fix struct type resolution with same-name contract imports Fix struct type resolution and SSA resilience for complex codebases Feb 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant