Skip to content

8.1. List functions in a contract#

This function finds a contract in the compilation unit with the given name and returns a list of FunctionDefinition within it. We use a combination of the Query API and the AST types:

list-functions-in-contract.mts
import { assertNonterminalNode, Query } from "@nomicfoundation/slang/cst";
import { ContractDefinition, FunctionDefinition } from "@nomicfoundation/slang/ast";
import { CompilationUnit } from "@nomicfoundation/slang/compilation";

export function listFunctionsInContract(unit: CompilationUnit, contractName: string): FunctionDefinition[] {
  for (const file of unit.files()) {
    const cursor = file.createTreeCursor();
    const query = Query.create(`
      [ContractDefinition
        @name name: [Identifier]
      ]
    `);
    const matches = cursor.query([query]);

    for (const match of matches) {
      const contractNode = match.root.node;
      assertNonterminalNode(contractNode);
      const name = match.captures["name"][0].node.unparse();
      if (name == contractName) {
        // found the contract
        const contract = new ContractDefinition(contractNode);
        const functions = contract.members.items
          .map((member) => member.variant)
          .filter((member) => member instanceof FunctionDefinition);
        return functions;
      }
    }
  }

  throw new Error(`Could not find contract named ${contractName}`);
}

From the list of FunctionDefinition nodes, it's easy to obtain the names of the functions:

test-list-functions.mts
import assert from "node:assert";
import { listFunctionsInContract } from "./list-functions-in-contract.mjs";
import buildSampleCompilationUnit from "../../common/sample-contract.mjs";

test("list functions by contract name", async () => {
  const unit = await buildSampleCompilationUnit();

  const functions = listFunctionsInContract(unit, "Counter");
  const functionNames = functions.map((fun) => fun.name.cst.unparse().trim());
  assert.deepEqual(functionNames, ["count", "increment"]);
});