Skip to content

Commit 1afeb91

Browse files
authored
Ignore function node IDs not found when validating parent initializer calls (#1137)
1 parent a3b4ef9 commit 1afeb91

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

packages/core/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Changelog
22

3+
## 1.42.2 (2025-03-19)
4+
5+
- Fix `ASTDereferencerError` for additional scenario when validating initializers. ([#1137](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1137))
6+
37
## 1.42.1 (2025-01-24)
48

5-
- Fix `ASTDereferencerError` when validating initializers.
9+
- Fix `ASTDereferencerError` when validating initializers. ([#1118](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1118))
610

711
## 1.42.0 (2025-01-23)
812

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openzeppelin/upgrades-core",
3-
"version": "1.42.1",
3+
"version": "1.42.2",
44
"description": "",
55
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/core",
66
"license": "MIT",

packages/core/src/validate/run.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,10 @@ function setCachedOpcodes(key: number, scope: string, cache: OpcodeCache, errors
512512
}
513513
}
514514

515-
function tryDerefFunction(deref: ASTDereferencer, referencedDeclaration: number): FunctionDefinition | undefined {
515+
export function tryDerefFunction(
516+
deref: ASTDereferencer,
517+
referencedDeclaration: number,
518+
): FunctionDefinition | undefined {
516519
try {
517520
return deref(['FunctionDefinition'], referencedDeclaration);
518521
} catch (e: any) {

packages/core/src/validate/run/initializer.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ContractDefinition, FunctionDefinition } from 'solidity-ast';
22
import { ASTDereferencer, findAll } from 'solidity-ast/utils';
33
import { SrcDecoder } from '../../src-decoder';
4-
import { ValidationExceptionInitializer, skipCheck } from '../run';
4+
import { ValidationExceptionInitializer, skipCheck, tryDerefFunction } from '../run';
55

66
/**
77
* Reports if this contract is non-abstract and any of the following are true:
@@ -141,6 +141,27 @@ function getParentsNotInitializedByOtherParents(
141141
return remainingParents;
142142
}
143143

144+
/**
145+
* Calls the callback if the referenced function definition is found in the AST.
146+
* Otherwise, does nothing.
147+
*
148+
* @param deref AST dereferencer
149+
* @param referencedDeclaration ID of the referenced function
150+
* @param callback Function to call if the referenced function definition is found
151+
*/
152+
function doIfReferencedFunctionFound(
153+
deref: ASTDereferencer,
154+
referencedDeclaration: number | null | undefined,
155+
callback: (functionDef: FunctionDefinition) => void,
156+
) {
157+
if (referencedDeclaration && referencedDeclaration > 0) {
158+
const functionDef = tryDerefFunction(deref, referencedDeclaration);
159+
if (functionDef !== undefined) {
160+
callback(functionDef);
161+
}
162+
}
163+
}
164+
144165
/**
145166
* Reports exceptions for missing initializer calls, duplicate initializer calls, and incorrect initializer order.
146167
*
@@ -176,10 +197,9 @@ function* getInitializerCallExceptions(
176197
(fnCall.expression.nodeType === 'Identifier' || fnCall.expression.nodeType === 'MemberAccess')
177198
) {
178199
let recursiveFunctionIds: number[] = [];
179-
const referencedFn = fnCall.expression.referencedDeclaration;
180-
if (referencedFn && referencedFn > 0) {
181-
recursiveFunctionIds = getRecursiveFunctionIds(referencedFn, deref);
182-
}
200+
doIfReferencedFunctionFound(deref, fnCall.expression.referencedDeclaration, (functionDef: FunctionDefinition) => {
201+
recursiveFunctionIds = getRecursiveFunctionIds(deref, functionDef);
202+
});
183203

184204
// For each recursively called function, if it is a parent initializer, then:
185205
// - Check if it was already called (duplicate call)
@@ -258,38 +278,41 @@ function* getInitializerCallExceptions(
258278
/**
259279
* Gets the IDs of all functions that are recursively called by the given function, including the given function itself at the end of the list.
260280
*
261-
* @param referencedFn The ID of the function to start from
262281
* @param deref AST dereferencer
282+
* @param functionDef The node of the function definition to start from
263283
* @param visited Set of function IDs that have already been visited
264284
* @returns The IDs of all functions that are recursively called by the given function, including the given function itself at the end of the list.
265285
*/
266-
function getRecursiveFunctionIds(referencedFn: number, deref: ASTDereferencer, visited?: Set<number>): number[] {
286+
function getRecursiveFunctionIds(
287+
deref: ASTDereferencer,
288+
functionDef: FunctionDefinition,
289+
visited?: Set<number>,
290+
): number[] {
267291
const result: number[] = [];
268292

269293
if (visited === undefined) {
270294
visited = new Set();
271295
}
272-
if (visited.has(referencedFn)) {
296+
if (visited.has(functionDef.id)) {
273297
return result;
274298
} else {
275-
visited.add(referencedFn);
299+
visited.add(functionDef.id);
276300
}
277301

278-
const fn = deref('FunctionDefinition', referencedFn);
279-
const expressionStatements = fn.body?.statements?.filter(stmt => stmt.nodeType === 'ExpressionStatement') ?? [];
302+
const expressionStatements =
303+
functionDef.body?.statements?.filter(stmt => stmt.nodeType === 'ExpressionStatement') ?? [];
280304
for (const stmt of expressionStatements) {
281305
const fnCall = stmt.expression;
282306
if (
283307
fnCall.nodeType === 'FunctionCall' &&
284308
(fnCall.expression.nodeType === 'Identifier' || fnCall.expression.nodeType === 'MemberAccess')
285309
) {
286-
const referencedId = fnCall.expression.referencedDeclaration;
287-
if (referencedId && referencedId > 0) {
288-
result.push(...getRecursiveFunctionIds(referencedId, deref, visited));
289-
}
310+
doIfReferencedFunctionFound(deref, fnCall.expression.referencedDeclaration, (functionDef: FunctionDefinition) => {
311+
result.push(...getRecursiveFunctionIds(deref, functionDef, visited));
312+
});
290313
}
291314
}
292-
result.push(referencedFn);
315+
result.push(functionDef.id);
293316

294317
return result;
295318
}

0 commit comments

Comments
 (0)