inheritance-ordering.js 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. #!/usr/bin/env node
  2. const path = require('path');
  3. const graphlib = require('graphlib');
  4. const match = require('micromatch');
  5. const { findAll } = require('solidity-ast/utils');
  6. const { _: artifacts } = require('yargs').argv;
  7. // files to skip
  8. const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**'];
  9. for (const artifact of artifacts) {
  10. const { output: solcOutput } = require(path.resolve(__dirname, '../..', artifact));
  11. const graph = new graphlib.Graph({ directed: true });
  12. const names = {};
  13. const linearized = [];
  14. for (const source in solcOutput.contracts) {
  15. if (match.any(source, skipPatterns)) continue;
  16. for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) {
  17. names[contractDef.id] = contractDef.name;
  18. linearized.push(contractDef.linearizedBaseContracts);
  19. contractDef.linearizedBaseContracts.forEach((c1, i, contracts) =>
  20. contracts.slice(i + 1).forEach(c2 => {
  21. graph.setEdge(c1, c2);
  22. }),
  23. );
  24. }
  25. }
  26. /// graphlib.alg.findCycles will not find minimal cycles.
  27. /// We are only interested int cycles of lengths 2 (needs proof)
  28. graph.nodes().forEach((x, i, nodes) =>
  29. nodes
  30. .slice(i + 1)
  31. .filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x))
  32. .forEach(y => {
  33. console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`);
  34. linearized
  35. .filter(chain => chain.includes(parseInt(x)) && chain.includes(parseInt(y)))
  36. .forEach(chain => {
  37. const comp = chain.indexOf(parseInt(x)) < chain.indexOf(parseInt(y)) ? '>' : '<';
  38. console.log(`- ${names[x]} ${comp} ${names[y]} in ${names[chain.find(Boolean)]}`);
  39. // console.log(`- ${names[x]} ${comp} ${names[y]}: ${chain.reverse().map(id => names[id]).join(', ')}`);
  40. });
  41. process.exitCode = 1;
  42. }),
  43. );
  44. }
  45. if (!process.exitCode) {
  46. console.log('Contract ordering is consistent.');
  47. }