From 6ae568bce126d0b48781ad9c426f93ce124719ae Mon Sep 17 00:00:00 2001 From: Valery Aligorsky Date: Mon, 7 Feb 2022 20:57:03 +0100 Subject: [PATCH] feat(sol-0.8): add support for export of user defined types at root level --- lib/antlr/visitors/exportVisitor.ts | 28 +++++++++++++++++++ lib/antlr/visitors/types.ts | 9 +++++- lib/exportsAnalyzer.ts | 27 +++++++++++++++++- lib/fileAnalyzer.ts | 4 +++ .../ContractWithUserDefinitionType.sol | 8 ++++++ .../ContractWithUserDefinitionType.sol | 7 +++++ test/exportsAnalyzer.spec.ts | 16 +++++++++++ test/index.spec.ts | 4 +++ 8 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 test/compiled/ContractWithUserDefinitionType.sol create mode 100644 test/contracts/ContractWithUserDefinitionType.sol diff --git a/lib/antlr/visitors/exportVisitor.ts b/lib/antlr/visitors/exportVisitor.ts index ecb891d..137f83a 100644 --- a/lib/antlr/visitors/exportVisitor.ts +++ b/lib/antlr/visitors/exportVisitor.ts @@ -15,6 +15,7 @@ import { SolidityParser, SourceUnitContext, StructDefinitionContext, + UserDefinedValueTypeDefinitionContext, } from '../generated/SolidityParser'; import { ExportVisitResult, VisitCallback } from './types'; @@ -398,4 +399,31 @@ class ExportVisitor implements SolidityParserListener { name: name.text, }); } + + enterUserDefinedValueTypeDefinition( + ctx: UserDefinedValueTypeDefinitionContext, + ): void { + if (!(ctx.parent instanceof SourceUnitContext)) { + return; + } + + if (!ctx.stop) { + return; + } + + const start = ctx.start.startIndex; + const end = ctx.stop.stopIndex; + const name = ctx.identifier(); + + if (!name?.stop) { + return; + } + + this.#onVisit({ + type: ExportType.userDefinedValueType, + start, + end, + name: name.text, + }); + } } diff --git a/lib/antlr/visitors/types.ts b/lib/antlr/visitors/types.ts index 8809e4a..61bc0e1 100644 --- a/lib/antlr/visitors/types.ts +++ b/lib/antlr/visitors/types.ts @@ -19,7 +19,8 @@ export interface ImportVisitNamedImport { export type ExportVisitResult = | ExportVisitResultContractLike | ExportVisitResultConstant - | ExportVisitResultFunction; + | ExportVisitResultFunction + | ExportVisitResultUserDefinedValueType; export interface ExportVisitResultContractLike extends RangeVisitResult { abstract: boolean; @@ -41,4 +42,10 @@ export interface ExportVisitResultFunction extends RangeVisitResult { name: string; } +export interface ExportVisitResultUserDefinedValueType + extends RangeVisitResult { + type: ExportType.userDefinedValueType; + name: string; +} + export type VisitCallback = (v: T) => void; diff --git a/lib/exportsAnalyzer.ts b/lib/exportsAnalyzer.ts index b67a5bc..18a16bd 100644 --- a/lib/exportsAnalyzer.ts +++ b/lib/exportsAnalyzer.ts @@ -3,6 +3,7 @@ import { SolidityExportVisitor } from './antlr/visitors/exportVisitor'; import { ExportVisitResultConstant, ExportVisitResultFunction, + ExportVisitResultUserDefinedValueType, } from './antlr/visitors/types'; import { ContractLikeExportType, ExportType } from './types'; @@ -11,7 +12,8 @@ const error = Debug('sol-merger:error'); export type ExportsAnalyzerResult = | ExportsAnalyzerResultContractLike | ExportsAnalyzerResultConstant - | ExportsAnalyzerResultFunction; + | ExportsAnalyzerResultFunction + | ExportsAnalyzerResultUserDefinedValueType; export interface ExportsAnalyzerResultContractLike { abstract: boolean; @@ -34,6 +36,12 @@ export interface ExportsAnalyzerResultFunction { body: string; } +export interface ExportsAnalyzerResultUserDefinedValueType { + type: ExportType.userDefinedValueType; + name: string; + body: string; +} + export class ExportsAnalyzer { constructor(private contents: string) {} @@ -63,6 +71,13 @@ export class ExportsAnalyzer { results.push(functionExport); return; } + + if (e.type === ExportType.userDefinedValueType) { + const userDefinedValueTypeExport = + this.analyzeExportUserDefinedValueType(e); + results.push(userDefinedValueTypeExport); + return; + } results.push({ abstract: e.abstract, type: e.type, @@ -100,4 +115,14 @@ export class ExportsAnalyzer { type: ExportType.function, }; } + + private analyzeExportUserDefinedValueType( + e: ExportVisitResultUserDefinedValueType, + ): ExportsAnalyzerResultUserDefinedValueType { + return { + body: this.contents.substring(e.start, e.end + 1), + name: e.name, + type: ExportType.userDefinedValueType, + }; + } } diff --git a/lib/fileAnalyzer.ts b/lib/fileAnalyzer.ts index 02f52a9..1254013 100644 --- a/lib/fileAnalyzer.ts +++ b/lib/fileAnalyzer.ts @@ -28,6 +28,10 @@ export class FileAnalyzer { return e.body; } + if (e.type === ExportType.userDefinedValueType) { + return e.body; + } + let is = e.is; if (is) { globalRenames.forEach((i) => { diff --git a/test/compiled/ContractWithUserDefinitionType.sol b/test/compiled/ContractWithUserDefinitionType.sol new file mode 100644 index 0000000..6e12614 --- /dev/null +++ b/test/compiled/ContractWithUserDefinitionType.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.8.8; + + +type Decimal18 is uint256; + +interface MinimalERC20 { + function transfer(address to, Decimal18 value) external; +} diff --git a/test/contracts/ContractWithUserDefinitionType.sol b/test/contracts/ContractWithUserDefinitionType.sol new file mode 100644 index 0000000..eb1cb8a --- /dev/null +++ b/test/contracts/ContractWithUserDefinitionType.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.8.8; + +type Decimal18 is uint256; + +interface MinimalERC20 { + function transfer(address to, Decimal18 value) external; +} diff --git a/test/exportsAnalyzer.spec.ts b/test/exportsAnalyzer.spec.ts index c42da63..29d9316 100644 --- a/test/exportsAnalyzer.spec.ts +++ b/test/exportsAnalyzer.spec.ts @@ -167,5 +167,21 @@ describe('ExportsAnalyzer', () => { }, ]); }); + + it('should analyze user defined value type export', () => { + const exportsAnalyzer = new ExportsAnalyzer(` + type Decimal18 is uint256; + `); + + const exports = exportsAnalyzer.analyzeExports(); + + assert.deepEqual(exports, [ + { + name: 'Decimal18', + type: ExportType.userDefinedValueType, + body: `type Decimal18 is uint256;`, + }, + ]); + }); }); }); diff --git a/test/index.spec.ts b/test/index.spec.ts index dc28eb2..530dc0a 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -128,4 +128,8 @@ describe('Solidity Merger', () => { it('should compile file with functions at root level (0.8 support)', async () => { await testFile('ContractWithTopLevelFunction'); }); + + it('should compile file with user defined types at root level (0.8 support)', async () => { + await testFile('ContractWithUserDefinitionType'); + }); });