Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide helpers to list inherited functions (and other items) #433

Open
segfaultxavi opened this issue Feb 16, 2023 · 2 comments
Open

Provide helpers to list inherited functions (and other items) #433

segfaultxavi opened this issue Feb 16, 2023 · 2 comments

Comments

@segfaultxavi
Copy link

I can't find a simple way of retrieving all the methods available in a contract, including inherited ones.
I'm currently working on a helper method that iterates over all linearizedBaseContracts, retrieves their methods and merges them. But I need to take into account visibility, overrides... it starts to look like a lot of work and I kind of think such a basic thing should be available out of the box.
What am I missing?

@frangio
Copy link
Contributor

frangio commented Feb 16, 2023

You're right this should be available out of the box, there is quite a bit of complexity. I've implemented this for OpenZeppelin Contracts so you can reuse that but adding this in your templates/properties.js (or wherever you have your templates):

module.exports.inheritance = function ({ item, build }) {
  if (!isNodeType('ContractDefinition', item)) {
    throw new Error('used inherited-items on non-contract');
  }

  return item.linearizedBaseContracts
    .map(id => build.deref('ContractDefinition', id))
    .filter((c, i) => c.name !== 'Context' || i === 0);
};


module.exports['inherited-functions'] = function ({ item }) {
  const { inheritance } = item;
  const baseFunctions = new Set(inheritance.flatMap(c => c.functions.flatMap(f => f.baseFunctions ?? [])));
  return inheritance.map((contract, i) => ({
    contract,
    functions: contract.functions.filter(f => !baseFunctions.has(f.id) && (f.name !== 'constructor' || i === 0)),
  }));
};

Later use it in the contract.hbs template like {{#each inherited-functions}} ....

@frangio frangio changed the title How do I retrieve ALL methods, including inherited ones? Provide helpers to list inherited functions (and other items) Feb 16, 2023
@segfaultxavi
Copy link
Author

Thanks a lot for your answer! I've been tinkering all day (it's my first serious contact with TypeScript) and this is what I came up with:

export function allItems(this: DocItemWithContext, nodeTypeName: string) {
  if (this.nodeType == 'ContractDefinition') {

    const { deref } = this.__item_context.build;
    const parents = this.linearizedBaseContracts.map(deref('ContractDefinition'));
    let items: (EnumDefinition | ErrorDefinition | EventDefinition | FunctionDefinition | ModifierDefinition
      | StructDefinition | UserDefinedValueTypeDefinition | VariableDeclaration)[] = [];
    parents.forEach(p => {
      p.nodes.forEach(n => {
        // Filter out other types
        if (n.nodeType == 'UsingForDirective' || n.nodeType != nodeTypeName) return;
        // Filter out private fields
        if ((n.nodeType == 'VariableDeclaration' || n.nodeType == 'FunctionDefinition') &&
          (n.visibility != 'public' && n.visibility != 'external')) return;
        if (n.nodeType == 'FunctionDefinition' && n.virtual) return;
        // If this item already exists do not add it again.
        // linearizedBaseContracts returned the children first and then the parents, so if the item
        // already exists it means that it is an override, and we want to keep those (if they had any docs).
        const prev = items.find(i => i.name == n.name);
        const prevDocs = prev && (
          prev.nodeType == 'ErrorDefinition' ||
          prev.nodeType == 'EventDefinition' ||
          prev.nodeType == 'FunctionDefinition') ? prev.documentation : null;
        if (!prev || !prevDocs)
          items.push(n);
      });
    });

    items.sort((a, b) => a.name < b.name ? -1 : 1);
    return items;
  }
}

I've added this to helpers.ts and I use it with {{#each (allItems typeName)}} . I'm sure this stinks, I better study your code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants