diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index 010021eb26814..9b9c44add6160 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -1024,6 +1024,9 @@ impl<'ast> State<'_, 'ast> { self.print_assign_rhs(init, pre_init_size, init_space_left, Some(&ty.kind), cache); } else { + if override_.is_some() { + self.end(); + } self.end(); } self.end(); diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index 702e724599ba0..dd261168f94d2 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -250,3 +250,150 @@ contract ProofOfConcept { assert_eq!(formatted, expected, "Formatting mismatch"); } + +#[test] +fn test_override_state_variable_without_initializer_does_not_leak_indent() { + init_tracing(); + + let cases = [ + ( + "top-level items after override variable", + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total; +} + +struct Info { + uint256 a; +} + +function topLevel(uint256 value) pure returns (uint256) { + return value; +} +"#, + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total; +} + +struct Info { + uint256 a; +} + +function topLevel(uint256 value) pure returns (uint256) { + return value; +} +"#, + ), + ( + "contract member after override variable", + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total; + uint256 public next; +} +"#, + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total; + uint256 public next; +} +"#, + ), + ( + "override path list without initializer", + r#"pragma solidity ^0.8.28; + +contract BaseA { + uint256 public total; +} + +contract BaseB { + uint256 public total; +} + +contract ChildStorage is BaseA, BaseB { + uint256 public override(BaseA, BaseB) total; +} + +error AfterOverride(uint256 value); +"#, + r#"pragma solidity ^0.8.28; + +contract BaseA { + uint256 public total; +} + +contract BaseB { + uint256 public total; +} + +contract ChildStorage is BaseA, BaseB { + uint256 public override(BaseA, BaseB) total; +} + +error AfterOverride(uint256 value); +"#, + ), + ( + "override variable with initializer", + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total = 0; +} + +struct AfterInitializer { + uint256 a; +} +"#, + r#"pragma solidity ^0.8.28; + +contract BaseStorage { + uint256 public total; +} + +contract ChildStorage is BaseStorage { + uint256 public override total = 0; +} + +struct AfterInitializer { + uint256 a; +} +"#, + ), + ]; + + let fmt_config = Arc::new(FormatterConfig::default()); + let path = Path::new("override-indent.sol"); + + for (case, source, expected) in cases { + let formatted = format(source, path, fmt_config.clone()); + assert_eq!(formatted, expected, "{case}"); + assert_eq!(format(&formatted, path, fmt_config.clone()), expected, "{case} idempotency"); + } +}