@openzeppelin/upgrades-core 1.34.0 → 1.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/add-proxy-to-manifest.js +1 -2
  3. package/dist/add-proxy-to-manifest.js.map +1 -1
  4. package/dist/beacon.js +1 -2
  5. package/dist/beacon.js.map +1 -1
  6. package/dist/call-optional-signature.js +1 -2
  7. package/dist/call-optional-signature.js.map +1 -1
  8. package/dist/cli/validate/build-info-file.js +1 -2
  9. package/dist/cli/validate/build-info-file.js.map +1 -1
  10. package/dist/cli/validate/contract-report.js +2 -2
  11. package/dist/cli/validate/contract-report.js.map +1 -1
  12. package/dist/cli/validate/find-contract.js +2 -2
  13. package/dist/cli/validate/find-contract.js.map +1 -1
  14. package/dist/cli/validate/project-report.d.ts.map +1 -1
  15. package/dist/cli/validate/project-report.js +2 -2
  16. package/dist/cli/validate/project-report.js.map +1 -1
  17. package/dist/cli/validate/upgradeability-assessment.js +1 -2
  18. package/dist/cli/validate/upgradeability-assessment.js.map +1 -1
  19. package/dist/cli/validate/validate-upgrade-safety.js +3 -4
  20. package/dist/cli/validate/validate-upgrade-safety.js.map +1 -1
  21. package/dist/cli/validate/validations.js +1 -2
  22. package/dist/cli/validate/validations.js.map +1 -1
  23. package/dist/cli/validate.js +3 -4
  24. package/dist/cli/validate.js.map +1 -1
  25. package/dist/deployment.js +4 -4
  26. package/dist/deployment.js.map +1 -1
  27. package/dist/eip-1967-type.js +3 -4
  28. package/dist/eip-1967-type.js.map +1 -1
  29. package/dist/eip-1967.js +7 -7
  30. package/dist/eip-1967.js.map +1 -1
  31. package/dist/error.d.ts +0 -1
  32. package/dist/error.d.ts.map +1 -1
  33. package/dist/impl-address.js +3 -3
  34. package/dist/impl-address.js.map +1 -1
  35. package/dist/impl-store.js +5 -6
  36. package/dist/impl-store.js.map +1 -1
  37. package/dist/levenshtein.js +1 -2
  38. package/dist/levenshtein.js.map +1 -1
  39. package/dist/link-refs.js +2 -3
  40. package/dist/link-refs.js.map +1 -1
  41. package/dist/manifest-storage-layout.js +2 -3
  42. package/dist/manifest-storage-layout.js.map +1 -1
  43. package/dist/manifest.js +3 -3
  44. package/dist/manifest.js.map +1 -1
  45. package/dist/provider.js +15 -15
  46. package/dist/provider.js.map +1 -1
  47. package/dist/proxy-kind.js +3 -4
  48. package/dist/proxy-kind.js.map +1 -1
  49. package/dist/scripts/migrate-oz-cli-project.js +4 -5
  50. package/dist/scripts/migrate-oz-cli-project.js.map +1 -1
  51. package/dist/src-decoder.js +1 -2
  52. package/dist/src-decoder.js.map +1 -1
  53. package/dist/storage/compare.js +4 -4
  54. package/dist/storage/compare.js.map +1 -1
  55. package/dist/storage/extract.js +4 -5
  56. package/dist/storage/extract.js.map +1 -1
  57. package/dist/storage/gap.js +2 -3
  58. package/dist/storage/gap.js.map +1 -1
  59. package/dist/storage/index.js +4 -4
  60. package/dist/storage/index.js.map +1 -1
  61. package/dist/storage/layout.js +4 -5
  62. package/dist/storage/layout.js.map +1 -1
  63. package/dist/storage/namespace.js +3 -4
  64. package/dist/storage/namespace.js.map +1 -1
  65. package/dist/stub-provider.d.ts.map +1 -1
  66. package/dist/stub-provider.js +1 -2
  67. package/dist/stub-provider.js.map +1 -1
  68. package/dist/upgrade-interface-version.d.ts +0 -1
  69. package/dist/upgrade-interface-version.d.ts.map +1 -1
  70. package/dist/upgrade-interface-version.js +1 -2
  71. package/dist/upgrade-interface-version.js.map +1 -1
  72. package/dist/usage-error.js +2 -2
  73. package/dist/usage-error.js.map +1 -1
  74. package/dist/utils/address.js +1 -2
  75. package/dist/utils/address.js.map +1 -1
  76. package/dist/utils/annotations.js +3 -4
  77. package/dist/utils/annotations.js.map +1 -1
  78. package/dist/utils/assert.js +2 -3
  79. package/dist/utils/assert.js.map +1 -1
  80. package/dist/utils/contract-name.js +1 -2
  81. package/dist/utils/contract-name.js.map +1 -1
  82. package/dist/utils/curry.js +1 -2
  83. package/dist/utils/curry.js.map +1 -1
  84. package/dist/utils/deep-array.js +1 -2
  85. package/dist/utils/deep-array.js.map +1 -1
  86. package/dist/utils/execall.js +1 -2
  87. package/dist/utils/execall.js.map +1 -1
  88. package/dist/utils/function.js +1 -2
  89. package/dist/utils/function.js.map +1 -1
  90. package/dist/utils/indent.js +1 -2
  91. package/dist/utils/indent.js.map +1 -1
  92. package/dist/utils/is-nullish.js +1 -2
  93. package/dist/utils/is-nullish.js.map +1 -1
  94. package/dist/utils/is-value-type.js +1 -2
  95. package/dist/utils/is-value-type.js.map +1 -1
  96. package/dist/utils/itemize.js +2 -3
  97. package/dist/utils/itemize.js.map +1 -1
  98. package/dist/utils/log.js +3 -4
  99. package/dist/utils/log.js.map +1 -1
  100. package/dist/utils/make-namespaced.d.ts.map +1 -1
  101. package/dist/utils/make-namespaced.js +41 -21
  102. package/dist/utils/make-namespaced.js.map +1 -1
  103. package/dist/utils/make-namespaced.test.js +1 -1
  104. package/dist/utils/make-namespaced.test.js.map +1 -1
  105. package/dist/utils/make-non-enumerable.js +1 -2
  106. package/dist/utils/make-non-enumerable.js.map +1 -1
  107. package/dist/utils/map-values.js +1 -2
  108. package/dist/utils/map-values.js.map +1 -1
  109. package/dist/utils/parse-type-id.js +1 -2
  110. package/dist/utils/parse-type-id.js.map +1 -1
  111. package/dist/utils/pick.js +1 -2
  112. package/dist/utils/pick.js.map +1 -1
  113. package/dist/utils/stabilize-layout.d.ts +15 -15
  114. package/dist/utils/stabilize-layout.js +1 -2
  115. package/dist/utils/stabilize-layout.js.map +1 -1
  116. package/dist/utils/type-id.js +3 -4
  117. package/dist/utils/type-id.js.map +1 -1
  118. package/dist/validate/data.js +3 -4
  119. package/dist/validate/data.js.map +1 -1
  120. package/dist/validate/overrides.js +3 -3
  121. package/dist/validate/overrides.js.map +1 -1
  122. package/dist/validate/query.js +12 -13
  123. package/dist/validate/query.js.map +1 -1
  124. package/dist/validate/run.d.ts.map +1 -1
  125. package/dist/validate/run.js +29 -5
  126. package/dist/validate/run.js.map +1 -1
  127. package/dist/validate.test.js +26 -15
  128. package/dist/validate.test.js.map +1 -1
  129. package/dist/version.js +3 -4
  130. package/dist/version.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/utils/make-namespaced.test.ts +1 -1
  133. package/src/utils/make-namespaced.test.ts.md +46 -23
  134. package/src/utils/make-namespaced.test.ts.snap +0 -0
  135. package/src/utils/make-namespaced.ts +44 -18
  136. package/src/validate/run.ts +30 -1
  137. package/src/validate.test.ts +38 -18
@@ -46,22 +46,22 @@ Generated by [AVA](https://avajs.dev).
46
46
  } SecondaryStorage $SecondaryStorage_random;␊
47
47
  ␊
48
48
  /// @notice some natspec␊
49
- enum $astId_id_random { dummy }␊
49
+ function foo() public {}␊
50
50
  ␊
51
51
  /// @param a docs␊
52
- enum $astId_id_random { dummy }␊
52
+ function foo1(uint a) public {}␊
53
53
  ␊
54
54
  /// @param a docs␊
55
- enum $astId_id_random { dummy }␊
55
+ function foo2(uint a) internal {}␊
56
56
  struct MyStruct { uint b; }␊
57
57
  ␊
58
58
  // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));␊
59
59
  bytes32 private constant MAIN_STORAGE_LOCATION =␊
60
60
  0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;␊
61
61
  ␊
62
- enum $astId_id_random { dummy }␊
62
+ function _getMainStorage() private pure returns (bool) {}␊
63
63
  ␊
64
- enum $astId_id_random { dummy }␊
64
+ function _getXTimesY() internal view returns (bool) {}␊
65
65
  ␊
66
66
  /// @notice standlone natspec␊
67
67
  ␊
@@ -77,14 +77,14 @@ Generated by [AVA](https://avajs.dev).
77
77
  /**␊
78
78
  * doc block without natspec␊
79
79
  */␊
80
- enum $astId_id_random { dummy }␊
80
+ function foo3() public {}␊
81
81
  ␊
82
82
  /**␊
83
83
  * doc block with natspec␊
84
84
  *␊
85
85
  * @notice some natspec␊
86
86
  */␊
87
- enum $astId_id_random { dummy }␊
87
+ function foo4() public {}␊
88
88
  ␊
89
89
  /**␊
90
90
  * @dev a custom error inside a contract␊
@@ -99,35 +99,39 @@ Generated by [AVA](https://avajs.dev).
99
99
  }␊
100
100
  ␊
101
101
  contract HasFunction {␊
102
- enum $astId_id_random { dummy }␊
103
- enum $astId_id_random { dummy }␊
102
+ ␊
103
+ function foo() pure public returns (bool) {}␊
104
104
  }␊
105
105
  ␊
106
106
  contract UsingFunction is HasFunction {␊
107
107
  enum $astId_id_random { dummy }␊
108
108
  }␊
109
109
  ␊
110
- enum FreeFunctionUsingSelector { dummy }␊
110
+ function FreeFunctionUsingSelector() pure returns (bool) {}␊
111
111
  ␊
112
- enum CONSTANT_USING_SELECTOR { dummy }␊
112
+ bytes4 constant CONSTANT_USING_SELECTOR = HasFunction.foo.selector;␊
113
113
  ␊
114
114
  library Lib {␊
115
- enum $astId_id_random { dummy }␊
115
+ function usingSelector() pure public returns (bool) {}␊
116
116
  ␊
117
- enum $astId_id_random { dummy }␊
117
+ function plusOne(uint x) pure public returns (bool) {}␊
118
118
  }␊
119
119
  ␊
120
120
  contract Consumer {␊
121
121
  enum $astId_id_random { dummy }␊
122
122
  ␊
123
- enum $astId_id_random { dummy }␊
123
+ function usingFreeFunction() pure public returns (bool) {}␊
124
124
  ␊
125
- enum $astId_id_random { dummy }␊
125
+ function usingConstant() pure public returns (bool) {}␊
126
126
  ␊
127
- enum $astId_id_random { dummy }␊
127
+ function usingLibraryFunction() pure public returns (bool) {}␊
128
128
  }␊
129
129
  ␊
130
- enum plusTwo { dummy }␊
130
+ contract HasConstantWithSelector {␊
131
+ bytes4 constant CONTRACT_CONSTANT_USING_SELECTOR = HasFunction.foo.selector;␊
132
+ }␊
133
+ ␊
134
+ function plusTwo(uint x) pure returns (bool) {}␊
131
135
  ␊
132
136
  /**␊
133
137
  * @notice originally orphaned natspec␊
@@ -137,7 +141,7 @@ Generated by [AVA](https://avajs.dev).
137
141
  * @dev plusThree␊
138
142
  * @param x x␊
139
143
  */␊
140
- enum plusThree { dummy }␊
144
+ function plusThree(uint x) pure returns (bool) {}␊
141
145
  ␊
142
146
  /// @notice originally orphaned natspec 2␊
143
147
  ␊
@@ -146,9 +150,9 @@ Generated by [AVA](https://avajs.dev).
146
150
  * @param x x␊
147
151
  * @param y y␊
148
152
  */␊
149
- enum $astId_id_random { dummy }␊
153
+ function plusThree(uint x, uint y) pure returns (bool) {}␊
150
154
  ␊
151
- enum originallyNoDocumentation { dummy }␊
155
+ function originallyNoDocumentation() pure {}␊
152
156
  ␊
153
157
  /**␊
154
158
  * @param foo foo␊
@@ -158,7 +162,7 @@ Generated by [AVA](https://avajs.dev).
158
162
  contract UsingForDirectives {␊
159
163
  enum $astId_id_random { dummy }␊
160
164
  ␊
161
- enum $astId_id_random { dummy }␊
165
+ function usingFor(uint x) pure public returns (bool) {}␊
162
166
  }␊
163
167
  ␊
164
168
  /**␊
@@ -179,19 +183,38 @@ Generated by [AVA](https://avajs.dev).
179
183
  */␊
180
184
  enum CustomErrorOutsideContract { dummy }␊
181
185
  ␊
186
+ int8 constant MAX_SIZE_C = 2;␊
187
+ ␊
182
188
  contract StructArrayUsesConstant {␊
183
189
  uint16 private constant MAX_SIZE = 10;␊
184
190
  ␊
185
191
  struct NotNamespaced {␊
186
192
  uint16 a;␊
187
193
  uint256[MAX_SIZE] b;␊
194
+ uint256[MAX_SIZE_C] c;␊
188
195
  }␊
189
196
  ␊
190
197
  /// @custom:storage-location erc7201:uses.constant␊
191
198
  struct MainStorage {␊
192
199
  uint256 x;␊
193
200
  uint256[MAX_SIZE] y;␊
201
+ uint256[MAX_SIZE_C] c;␊
194
202
  } MainStorage $MainStorage_random;␊
203
+ }␊
204
+ ␊
205
+ address constant MY_ADDRESS = address(0);␊
206
+ uint constant CONVERTED_ADDRESS = uint160(MY_ADDRESS);␊
207
+ ␊
208
+ interface IDummy {␊
209
+ }␊
210
+ ␊
211
+ contract UsesAddress {␊
212
+ IDummy public constant MY_CONTRACT = IDummy(MY_ADDRESS);␊
213
+ }␊
214
+ ␊
215
+ contract HasFunctionWithRequiredReturn {␊
216
+ struct S { uint x; }␊
217
+ function foo(S calldata s) internal pure returns (bool) {}␊
195
218
  }`,
196
219
  },
197
220
  'contracts/test/NamespacedToModifyImported.sol': {
@@ -234,8 +257,8 @@ Generated by [AVA](https://avajs.dev).
234
257
  pragma solidity 0.7.6;␊
235
258
  ␊
236
259
  contract HasFunction {␊
237
- enum $astId_id_random { dummy }␊
238
- enum $astId_id_random { dummy }␊
260
+ ␊
261
+ function foo() pure public returns (bool) {}␊
239
262
  }␊
240
263
  ␊
241
264
  contract UsingFunction is HasFunction {␊
@@ -3,6 +3,7 @@ import { Node } from 'solidity-ast/node';
3
3
  import { SolcInput, SolcOutput } from '../solc-api';
4
4
  import { getStorageLocationAnnotation } from '../storage/namespace';
5
5
  import { assert } from './assert';
6
+ import { FunctionDefinition } from 'solidity-ast';
6
7
 
7
8
  const OUTPUT_SELECTION = {
8
9
  '*': {
@@ -40,7 +41,6 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcI
40
41
 
41
42
  const orig = Buffer.from(source.content, 'utf8');
42
43
 
43
- const replacedIdentifiers = new Set<string>();
44
44
  const modifications: Modification[] = [];
45
45
 
46
46
  for (const node of output.sources[sourcePath].ast.nodes) {
@@ -60,6 +60,10 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcI
60
60
  const contractNodes = contractDef.nodes;
61
61
  for (const contractNode of contractNodes) {
62
62
  switch (contractNode.nodeType) {
63
+ case 'FunctionDefinition': {
64
+ replaceFunction(contractNode, orig, modifications);
65
+ break;
66
+ }
63
67
  case 'VariableDeclaration': {
64
68
  // If variable is a constant, keep it since it may be referenced in a struct
65
69
  if (contractNode.constant) {
@@ -69,7 +73,6 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcI
69
73
  }
70
74
  case 'ErrorDefinition':
71
75
  case 'EventDefinition':
72
- case 'FunctionDefinition':
73
76
  case 'ModifierDefinition':
74
77
  case 'UsingForDirective': {
75
78
  // Replace with an enum based on astId (the original name is not needed, since nothing should reference it)
@@ -100,27 +103,30 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcI
100
103
  break;
101
104
  }
102
105
 
103
- // - UsingForDirective isn't needed, but it might have NatSpec documentation which is not included in the AST.
104
- // We convert it to a dummy enum to avoid orphaning any possible documentation.
105
- // - ErrorDefinition, FunctionDefinition, and VariableDeclaration might be imported by other files, so they cannot be deleted.
106
+ case 'FunctionDefinition': {
107
+ replaceFunction(node, orig, modifications);
108
+ break;
109
+ }
110
+
111
+ // - VariableDeclaration and ErrorDefinition might be imported by other files, so they cannot be deleted.
106
112
  // However, we need to remove their values to avoid referencing other deleted nodes.
107
- // We do this by converting them to dummy enums, but avoiding duplicate names.
108
- case 'UsingForDirective':
109
- case 'ErrorDefinition':
110
- case 'FunctionDefinition':
113
+ // We do this by converting them to dummy enums with the same name.
111
114
  case 'VariableDeclaration': {
112
- // If an identifier with the same name was not previously written, replace with a dummy enum using its name.
113
- // Otherwise replace with an enum based on astId to avoid duplicate names, which can happen if there was overloading.
114
- // This does not need to check all identifiers from the original contract, since the original compilation
115
- // should have failed if there were conflicts in the first place.
116
- if ('name' in node && !replacedIdentifiers.has(node.name)) {
117
- modifications.push(makeReplace(node, orig, toDummyEnumWithName(node.name)));
118
- replacedIdentifiers.add(node.name);
119
- } else {
120
- modifications.push(makeReplace(node, orig, toDummyEnumWithAstId(node.id)));
115
+ // If variable is a constant, keep it since it may be referenced in a struct
116
+ if (node.constant) {
117
+ break;
121
118
  }
119
+ // Otherwise, fall through to convert to dummy enum
120
+ }
121
+ case 'ErrorDefinition': {
122
+ modifications.push(makeReplace(node, orig, toDummyEnumWithName(node.name)));
122
123
  break;
123
124
  }
125
+ // - UsingForDirective isn't needed, but it might have NatSpec documentation which is not included in the AST.
126
+ // We convert it to a dummy enum to avoid orphaning any possible documentation.
127
+ case 'UsingForDirective':
128
+ modifications.push(makeReplace(node, orig, toDummyEnumWithAstId(node.id)));
129
+ break;
124
130
  case 'EnumDefinition':
125
131
  case 'ImportDirective':
126
132
  case 'PragmaDirective':
@@ -158,6 +164,26 @@ function toDummyEnumWithAstId(astId: number) {
158
164
  return `enum $astId_${astId}_${(Math.random() * 1e6).toFixed(0)} { dummy }`;
159
165
  }
160
166
 
167
+ function replaceFunction(node: FunctionDefinition, orig: Buffer, modifications: Modification[]) {
168
+ if (node.kind === 'constructor') {
169
+ modifications.push(makeDelete(node, orig));
170
+ } else {
171
+ if (node.modifiers.length > 0) {
172
+ for (const modifier of node.modifiers) {
173
+ modifications.push(makeDelete(modifier, orig));
174
+ }
175
+ }
176
+
177
+ if (node.returnParameters.parameters.length > 0) {
178
+ modifications.push(makeReplace(node.returnParameters, orig, '(bool)'));
179
+ }
180
+
181
+ if (node.body) {
182
+ modifications.push(makeReplace(node.body, orig, '{}'));
183
+ }
184
+ }
185
+ }
186
+
161
187
  function getPositions(node: Node) {
162
188
  const [start, length] = node.src.split(':').map(Number);
163
189
  const end = start + length;
@@ -6,6 +6,7 @@ import type {
6
6
  StructDefinition,
7
7
  TypeName,
8
8
  UserDefinedTypeName,
9
+ VariableDeclaration,
9
10
  } from 'solidity-ast';
10
11
  import debug from '../utils/debug';
11
12
 
@@ -586,7 +587,7 @@ function* getInternalFunctionStorageErrors(
586
587
  ): Generator<ValidationError> {
587
588
  // Note: Solidity does not allow annotations for non-public state variables, nor recursive types for public variables,
588
589
  // so annotations cannot be used to skip these checks.
589
- for (const variableDec of findAll('VariableDeclaration', contractOrStructDef)) {
590
+ for (const variableDec of getVariableDeclarations(contractOrStructDef, visitedNodeIds)) {
590
591
  if (variableDec.typeName?.nodeType === 'FunctionTypeName' && variableDec.typeName.visibility === 'internal') {
591
592
  // Find internal function types directly in this node's scope
592
593
  yield {
@@ -608,6 +609,34 @@ function* getInternalFunctionStorageErrors(
608
609
  }
609
610
  }
610
611
 
612
+ /**
613
+ * Gets variables declared directly in a contract or in a struct definition.
614
+ *
615
+ * If this is a contract with struct definitions annotated with a storage location according to ERC-7201,
616
+ * then the struct members are also included.
617
+ */
618
+ function* getVariableDeclarations(
619
+ contractOrStructDef: ContractDefinition | StructDefinition,
620
+ visitedNodeIds: Set<number>,
621
+ ): Generator<VariableDeclaration, void, undefined> {
622
+ if (contractOrStructDef.nodeType === 'ContractDefinition') {
623
+ for (const node of contractOrStructDef.nodes) {
624
+ if (node.nodeType === 'VariableDeclaration') {
625
+ yield node;
626
+ } else if (
627
+ node.nodeType === 'StructDefinition' &&
628
+ getStorageLocationAnnotation(node) !== undefined &&
629
+ !visitedNodeIds.has(node.id)
630
+ ) {
631
+ visitedNodeIds.add(node.id);
632
+ yield* getVariableDeclarations(node, visitedNodeIds);
633
+ }
634
+ }
635
+ } else if (contractOrStructDef.nodeType === 'StructDefinition') {
636
+ yield* contractOrStructDef.members;
637
+ }
638
+ }
639
+
611
640
  /**
612
641
  * Recursively traverse array and mapping types to find user-defined types (which may be struct references).
613
642
  */
@@ -8,6 +8,7 @@ import {
8
8
  assertUpgradeSafe,
9
9
  ValidationOptions,
10
10
  RunValidation,
11
+ ValidationErrors,
11
12
  } from './validate';
12
13
  import { solcInputOutputDecoder } from './src-decoder';
13
14
 
@@ -23,6 +24,7 @@ test.before(async t => {
23
24
  'contracts/test/ValidationsNatspec.sol:HasNonEmptyConstructorNatspec1',
24
25
  'contracts/test/Proxiable.sol:ChildOfProxiable',
25
26
  'contracts/test/ValidationsUDVT.sol:ValidationsUDVT',
27
+ 'contracts/test/ValidationsFunctionPointers.sol:InternalFunctionPointer',
26
28
  ];
27
29
 
28
30
  t.context.validation = {} as RunValidation;
@@ -38,11 +40,21 @@ test.before(async t => {
38
40
  }
39
41
  });
40
42
 
41
- function testValid(name: string, kind: ValidationOptions['kind'], valid: boolean) {
42
- testOverride(name, kind, {}, valid);
43
+ function testValid(name: string, kind: ValidationOptions['kind'], valid: boolean, numExpectedErrors?: number) {
44
+ testOverride(name, kind, {}, valid, numExpectedErrors);
43
45
  }
44
46
 
45
- function testOverride(name: string, kind: ValidationOptions['kind'], opts: ValidationOptions, valid: boolean) {
47
+ function testOverride(
48
+ name: string,
49
+ kind: ValidationOptions['kind'],
50
+ opts: ValidationOptions,
51
+ valid: boolean,
52
+ numExpectedErrors?: number,
53
+ ) {
54
+ if (numExpectedErrors !== undefined && numExpectedErrors > 0 && valid) {
55
+ throw new Error('Cannot expect errors for a valid contract');
56
+ }
57
+
46
58
  const optKeys = Object.keys(opts);
47
59
  const describeOpts = optKeys.length > 0 ? '(' + optKeys.join(', ') + ')' : '';
48
60
  const testName = [valid ? 'accepts' : 'rejects', kind, name, describeOpts].join(' ');
@@ -52,7 +64,10 @@ function testOverride(name: string, kind: ValidationOptions['kind'], opts: Valid
52
64
  if (valid) {
53
65
  t.notThrows(assertUpgSafe);
54
66
  } else {
55
- t.throws(assertUpgSafe);
67
+ const error = t.throws(assertUpgSafe) as ValidationErrors;
68
+ if (numExpectedErrors !== undefined) {
69
+ t.is(error.errors.length, numExpectedErrors);
70
+ }
56
71
  }
57
72
  });
58
73
  }
@@ -140,18 +155,31 @@ testValid('TransitiveLibraryIsUnsafe', 'transparent', false);
140
155
  testValid('contracts/test/ValidationsSameNameSafe.sol:SameName', 'transparent', true);
141
156
  testValid('contracts/test/ValidationsSameNameUnsafe.sol:SameName', 'transparent', false);
142
157
 
143
- testValid('StructExternalFunctionPointer', 'transparent', true);
144
- testValid('StructInternalFunctionPointer', 'transparent', false);
145
- testValid('StructImpliedInternalFunctionPointer', 'transparent', false);
158
+ test('ambiguous name', t => {
159
+ const error = t.throws(() => getContractVersion(t.context.validation, 'SameName'));
160
+ t.is(
161
+ error?.message,
162
+ 'Contract SameName is ambiguous. Use one of the following:\n' +
163
+ 'contracts/test/ValidationsSameNameSafe.sol:SameName\n' +
164
+ 'contracts/test/ValidationsSameNameUnsafe.sol:SameName',
165
+ );
166
+ });
167
+
168
+ testValid('NamespacedExternalFunctionPointer', 'transparent', true);
169
+ testValid('NamespacedInternalFunctionPointer', 'transparent', false);
170
+ testValid('NamespacedInternalFunctionPointerUsed', 'transparent', false, 1);
171
+ testValid('StructInternalFunctionPointerUsed', 'transparent', false, 1);
172
+ testValid('NonNamespacedInternalFunctionPointer', 'transparent', true);
173
+ testValid('NamespacedImpliedInternalFunctionPointer', 'transparent', false);
146
174
  testOverride(
147
- 'StructImpliedInternalFunctionPointer',
175
+ 'NamespacedImpliedInternalFunctionPointer',
148
176
  'transparent',
149
177
  { unsafeAllow: ['internal-function-storage'] },
150
178
  true,
151
179
  );
152
180
 
153
181
  testValid('UsesStandaloneStructInternalFn', 'transparent', false);
154
- testValid('StructUsesStandaloneStructInternalFn', 'transparent', false);
182
+ testValid('NamespacedUsesStandaloneStructInternalFn', 'transparent', false);
155
183
  testValid('RecursiveStructInternalFn', 'transparent', false);
156
184
  testValid('MappingRecursiveStructInternalFn', 'transparent', false);
157
185
  testValid('ArrayRecursiveStructInternalFn', 'transparent', false);
@@ -163,12 +191,4 @@ testValid('InternalFunctionPointer', 'transparent', false);
163
191
  testValid('ImpliedInternalFunctionPointer', 'transparent', false);
164
192
  testOverride('ImpliedInternalFunctionPointer', 'transparent', { unsafeAllow: ['internal-function-storage'] }, true);
165
193
 
166
- test('ambiguous name', t => {
167
- const error = t.throws(() => getContractVersion(t.context.validation, 'SameName'));
168
- t.is(
169
- error?.message,
170
- 'Contract SameName is ambiguous. Use one of the following:\n' +
171
- 'contracts/test/ValidationsSameNameSafe.sol:SameName\n' +
172
- 'contracts/test/ValidationsSameNameUnsafe.sol:SameName',
173
- );
174
- });
194
+ testValid('FunctionWithInternalFunctionPointer', 'transparent', true);