Skip to content

Commit ccd4481

Browse files
committed
add error with namespaces + @Custom:storage-size
1 parent 231df41 commit ccd4481

File tree

6 files changed

+50
-15
lines changed

6 files changed

+50
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.3.27 (2023-08-29)
4+
5+
- Throw error when using `@custom:storage-size` along with namespaced storage.
6+
37
## 0.3.26 (2023-08-28)
48

59
- Change location of initializer functions when the original contract doesn't have a constructor. Previously it would be the start of the contract, before state variables. It is now placed immediately before the first function of the contract, if the contract has functions.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.20;
3+
4+
/// @custom:storage-size 52
5+
contract C1 {
6+
}

src/transform-namespaces.test.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const test = _test as TestFn<Context>;
1717
interface Context {
1818
solcInput: SolcInput;
1919
solcOutput: SolcOutput;
20-
transform: Transform;
20+
transformFile: (file: string) => Transform;
2121
}
2222

2323
test.serial.before('compile', async t => {
@@ -28,14 +28,27 @@ test.serial.before('compile', async t => {
2828
});
2929

3030
test.beforeEach('transform', async t => {
31-
t.context.transform = new Transform(t.context.solcInput, t.context.solcOutput);
31+
t.context.transformFile = (file: string) =>
32+
new Transform(t.context.solcInput, t.context.solcOutput, {
33+
exclude: source => source !== file,
34+
});
3235
});
3336

3437
test('add namespace', t => {
3538
const file = 'contracts/namespaces.sol';
36-
t.context.transform.apply(transformConstructor(() => true));
37-
t.context.transform.apply(removeLeftoverConstructorHead);
38-
t.context.transform.apply(removeStateVarInits);
39-
t.context.transform.apply(addNamespaceStruct(() => true));
40-
t.snapshot(t.context.transform.results()[file]);
39+
const transform = t.context.transformFile(file);
40+
transform.apply(transformConstructor(() => true));
41+
transform.apply(removeLeftoverConstructorHead);
42+
transform.apply(removeStateVarInits);
43+
transform.apply(addNamespaceStruct(() => true));
44+
t.snapshot(transform.results()[file]);
45+
});
46+
47+
test('error with @custom:storage-size', t => {
48+
const file = 'contracts/namespaces-error-storage-size.sol';
49+
const transform = t.context.transformFile(file);
50+
t.throws(
51+
() => transform.apply(addNamespaceStruct(() => true)),
52+
{ message: 'Cannot combine namespaces with @custom:storage-size annotations (contracts/namespaces-error-storage-size.sol:5)' },
53+
);
4154
});

src/transformations/add-namespace-struct.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { isStorageVariable } from './utils/is-storage-variable';
88
import { erc7201Location } from '../utils/erc7201';
99
import { contractStartPosition } from './utils/contract-start-position';
1010
import { Node } from 'solidity-ast/node';
11+
import { extractContractStorageSize } from '../utils/natspec';
1112

1213
export function getNamespaceStructName(contractName: string): string {
1314
return contractName + 'Storage';
@@ -22,6 +23,12 @@ export function addNamespaceStruct(include?: (source: string) => boolean) {
2223
const { error, resolver } = tools;
2324

2425
for (const contract of findAll('ContractDefinition', sourceUnit)) {
26+
const specifiesStorageSize = extractContractStorageSize(contract) !== undefined;
27+
28+
if (specifiesStorageSize) {
29+
throw tools.error(contract, "Cannot combine namespaces with @custom:storage-size annotations");
30+
}
31+
2532
let start = contractStartPosition(contract, tools);
2633

2734
let finished = false;

src/transformations/add-storage-gaps.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { getNodeBounds } from '../solc/ast-utils';
66
import { StorageLayout } from '../solc/input-output';
77
import { Transformation } from './type';
88
import { TransformerTools } from '../transform';
9-
import { extractNatspec } from '../utils/extractNatspec';
9+
import { extractContractStorageSize } from '../utils/natspec';
1010
import { decodeTypeIdentifier } from '../utils/type-id';
1111
import { parseTypeId } from '../utils/parse-type-id';
1212
import { ASTResolver } from '../ast-resolver';
@@ -21,12 +21,7 @@ export function* addStorageGaps(
2121
): Generator<Transformation> {
2222
for (const contract of findAll('ContractDefinition', sourceUnit)) {
2323
if (contract.contractKind === 'contract') {
24-
let targetSlots = DEFAULT_SLOT_COUNT;
25-
for (const entry of extractNatspec(contract)) {
26-
if (entry.title === 'custom' && entry.tag === 'storage-size') {
27-
targetSlots = parseInt(entry.args);
28-
}
29-
}
24+
const targetSlots = extractContractStorageSize(contract) ?? DEFAULT_SLOT_COUNT;
3025

3126
const gapSize = targetSlots - getContractSlotCount(contract, getLayout(contract), resolver);
3227

src/utils/extractNatspec.ts renamed to src/utils/natspec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StructuredDocumentation } from 'solidity-ast';
1+
import { ContractDefinition, StructuredDocumentation } from 'solidity-ast';
22
import { execall } from './execall';
33

44
interface NatspecTag {
@@ -26,3 +26,13 @@ export function* extractNatspec(node: {
2626
}
2727
}
2828
}
29+
30+
export function extractContractStorageSize(contract: ContractDefinition): number | undefined {
31+
let targetSlots;
32+
for (const entry of extractNatspec(contract)) {
33+
if (entry.title === 'custom' && entry.tag === 'storage-size') {
34+
targetSlots = parseInt(entry.args);
35+
}
36+
}
37+
return targetSlots;
38+
}

0 commit comments

Comments
 (0)