-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Summary
Add a printer that displays the C3 linearization order for contract inheritance, helping developers understand the method resolution order (MRO) and inheritance hierarchy in complex Solidity projects.
Motivation
Solidity uses C3 linearization for multiple inheritance resolution, which determines:
- Function override order - Which implementation is called
- State variable initialization order - Critical for contract correctness
- Constructor execution order - Important for proper initialization
- Super call resolution - Which parent implementation
super
refers to
Understanding C3 linearization is crucial but challenging:
- The algorithm is non-intuitive for complex hierarchies
- Order affects contract behavior significantly
- Mistakes can lead to unexpected function calls
- Critical for upgrade patterns and proxy contracts
C3 Linearization Background
C3 linearization creates a linear order from a directed acyclic graph (DAG) of inheritance, ensuring:
- Precedence preservation - Children come before parents
- Monotonicity - Consistent ordering across inheritance chains
Example Inheritance Scenario
// Diamond inheritance pattern
contract A { function foo() virtual {} }
contract B is A { function foo() override {} }
contract C is A { function foo() override {} }
contract D is B, C { function foo() override(B, C) {} }
// C3 Linearization for D: [D, C, B, A]
// This means C.foo() overrides B.foo() when both are inherited
Expected Output Formats
Basic Format
C3 Linearization for TokenVault
================================================
0. → [SELF] TokenVault (contracts/TokenVault.sol:45)
1. [BASE] Pausable (contracts/security/Pausable.sol:12)
2. [BASE] ReentrancyGuard (contracts/security/ReentrancyGuard.sol:8)
3. [BASE] AccessControl (contracts/access/AccessControl.sol:20)
4. [BASE] Context (contracts/utils/Context.sol:5)
5. [ROOT] ERC165 (contracts/interfaces/ERC165.sol:3)
Detailed Format (--verbose)
C3 Linearization for TokenVault
================================================
0. → [SELF] TokenVault (contracts/TokenVault.sol:45)
Functions: 15 | Modifiers: 2 | State Variables: 5
Constructor: ✓ Explicit
Overrides: deposit(), withdraw(), pause()
New Functions: emergencyWithdraw(), setFee()
1. [BASE] Pausable (contracts/security/Pausable.sol:12)
Functions: 4 | Modifiers: 1 | State Variables: 1
Constructor: ✗ None
Provides: pause(), unpause(), paused(), whenNotPaused modifier
Inherited by: TokenVault
2. [BASE] ReentrancyGuard (contracts/security/ReentrancyGuard.sol:8)
Functions: 0 | Modifiers: 1 | State Variables: 1
Constructor: ✓ Initializes _status
Provides: nonReentrant modifier
Note: Constructor sets initial reentrancy status
[... continues ...]
Constructor Execution Order:
-----------------------------
1. ERC165.constructor()
2. Context.constructor()
3. AccessControl.constructor()
4. ReentrancyGuard.constructor()
5. Pausable.constructor()
6. TokenVault.constructor(address _token, uint256 _fee)
Super Call Resolution:
----------------------
- super.deposit() in TokenVault calls → Pausable.deposit()
- super._msgSender() in TokenVault calls → Context._msgSender()
Advanced Features
1. Conflict Detection
Detect potential issues in linearization such as:
- Functions that might be shadowed unexpectedly
- Ambiguous super calls
- Constructor order dependencies
2. Constructor Order Analysis
Determine the exact order of constructor execution, which is the reverse of the linearization order.
3. Super Resolution Helper
Trace where super.function()
calls resolve to in the inheritance chain.
Command-Line Interface
# Basic linearization
slither . --print c3-linearization
# With details
slither . --print c3-linearization --verbose
# Specific contract
slither . --print c3-linearization --contract TokenVault
# Include interfaces
slither . --print c3-linearization --include-interfaces
# Show constructor order
slither . --print c3-linearization --show-constructors
# Detect conflicts
slither . --print c3-linearization --detect-conflicts
Test Cases
// Complex diamond inheritance
contract A { function foo() virtual {} }
contract B is A { function foo() override {} }
contract C is A { function foo() override {} }
contract D is B, C { function foo() override(B, C) {} }
// Expected output for D:
// 0. D (current contract)
// 1. C (rightmost parent)
// 2. B (next parent)
// 3. A (common base)
// Multiple inheritance levels
contract Base { }
contract Left is Base { }
contract Right is Base { }
contract Child is Left, Right { }
contract GrandChild is Child { }
// Expected output for GrandChild:
// 0. GrandChild
// 1. Child
// 2. Right
// 3. Left
// 4. Base
Benefits
- Understand override behavior - Know exactly which function is called
- Debug inheritance issues - Identify unexpected shadowing
- Verify constructor order - Ensure proper initialization
- Design better hierarchies - Avoid linearization conflicts
- Educational value - Learn C3 linearization through examples
Integration Features
- IDE Integration - Export to JSON for IDE tooltips
- Documentation Generation - Auto-generate inheritance docs
- Verification Tools - Validate upgrade compatibility
Priority
Medium - While not identifying vulnerabilities directly, understanding C3 linearization is crucial for correctly reasoning about contract behavior, especially in complex inheritance hierarchies common in DeFi protocols. This printer provides immediate educational value and helps prevent inheritance-related bugs. It's particularly valuable for upgrade patterns and proxy contracts where linearization order affects functionality.