Skip to content

Commit

Permalink
feat(using-for): add support for Using-For at root level
Browse files Browse the repository at this point in the history
  • Loading branch information
RyuuGan committed Mar 27, 2022
1 parent 2579237 commit fac2708
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 3 deletions.
25 changes: 25 additions & 0 deletions lib/antlr/visitors/exportVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SourceUnitContext,
StructDefinitionContext,
UserDefinedValueTypeDefinitionContext,
UsingDirectiveContext,
} from '../generated/SolidityParser';
import { ExportVisitResult, VisitCallback } from './types';

Expand Down Expand Up @@ -426,4 +427,28 @@ class ExportVisitor implements SolidityParserListener {
name: name.text,
});
}

enterUsingDirective(ctx: UsingDirectiveContext): void {
if (!(ctx.parent instanceof SourceUnitContext)) {
return;
}

if (!ctx.stop) {
return;
}

const start = ctx.start.startIndex;
const end = ctx.stop.stopIndex;
const names = ctx.identifierPath();

const name =
names.map((n) => n.text).join(',') + `$${ExportType.usingDirective}`;

this.#onVisit({
type: ExportType.usingDirective,
start,
end,
name,
});
}
}
8 changes: 7 additions & 1 deletion lib/antlr/visitors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export type ExportVisitResult =
| ExportVisitResultContractLike
| ExportVisitResultConstant
| ExportVisitResultFunction
| ExportVisitResultUserDefinedValueType;
| ExportVisitResultUserDefinedValueType
| ExportVisitResultUsingDirective;

export interface ExportVisitResultContractLike extends RangeVisitResult {
abstract: boolean;
Expand Down Expand Up @@ -48,4 +49,9 @@ export interface ExportVisitResultUserDefinedValueType
name: string;
}

export interface ExportVisitResultUsingDirective extends RangeVisitResult {
type: ExportType.usingDirective;
name: string;
}

export type VisitCallback<T extends RangeVisitResult> = (v: T) => void;
25 changes: 24 additions & 1 deletion lib/exportsAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ExportVisitResultConstant,
ExportVisitResultFunction,
ExportVisitResultUserDefinedValueType,
ExportVisitResultUsingDirective,
} from './antlr/visitors/types';
import { ContractLikeExportType, ExportType } from './types';

Expand All @@ -13,7 +14,8 @@ export type ExportsAnalyzerResult =
| ExportsAnalyzerResultContractLike
| ExportsAnalyzerResultConstant
| ExportsAnalyzerResultFunction
| ExportsAnalyzerResultUserDefinedValueType;
| ExportsAnalyzerResultUserDefinedValueType
| ExportsAnalyzerResultUsingDirective;

export interface ExportsAnalyzerResultContractLike {
abstract: boolean;
Expand Down Expand Up @@ -42,6 +44,12 @@ export interface ExportsAnalyzerResultUserDefinedValueType {
body: string;
}

export interface ExportsAnalyzerResultUsingDirective {
type: ExportType.usingDirective;
name: string;
body: string;
}

export class ExportsAnalyzer {
constructor(private contents: string) {}

Expand Down Expand Up @@ -78,6 +86,11 @@ export class ExportsAnalyzer {
results.push(userDefinedValueTypeExport);
return;
}
if (e.type === ExportType.usingDirective) {
const usingDirectiveExport = this.analyzeUsingDirective(e);
results.push(usingDirectiveExport);
return;
}
results.push({
abstract: e.abstract,
type: e.type,
Expand Down Expand Up @@ -125,4 +138,14 @@ export class ExportsAnalyzer {
type: ExportType.userDefinedValueType,
};
}

private analyzeUsingDirective(
e: ExportVisitResultUsingDirective,
): ExportsAnalyzerResultUsingDirective {
return {
body: this.contents.substring(e.start, e.end + 1),
name: e.name,
type: ExportType.usingDirective,
};
}
}
4 changes: 4 additions & 0 deletions lib/fileAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export class FileAnalyzer {
return e.body;
}

if (e.type === ExportType.usingDirective) {
return e.body;
}

let is = e.is;
if (is) {
globalRenames.forEach((i) => {
Expand Down
6 changes: 5 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ export enum ExportType {
constant = 'constant',
function = 'function',
userDefinedValueType = 'userDefinedValueType',
usingDirective = 'usingDirective',
}

export type ContractLikeExportType = Exclude<
ExportType,
ExportType.constant | ExportType.function | ExportType.userDefinedValueType
| ExportType.constant
| ExportType.function
| ExportType.userDefinedValueType
| ExportType.usingDirective
>;

export interface ExportPluginProcessor {
Expand Down
51 changes: 51 additions & 0 deletions test/compiled/UsingDirectiveContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pragma solidity ^0.8.13;


struct Data { mapping(uint => bool) flags; }

// Now we attach functions to the type.
// The attached functions can be used throughout the rest of the module.
// If you import the module, you have to
// repeat the using directive there, for example as
// import "flags.sol" as Flags;
// using {Flags.insert, Flags.remove, Flags.contains}
// for Flags.Data;
using {insert, remove, contains} for Data;

function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}

function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}

function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}

contract C {
Data knownValues;

function register(uint value) public {
// Here, all variables of type Data have
// corresponding member functions.
// The following function call is identical to
// `Set.insert(knownValues, value)`
require(knownValues.insert(value));
}
}
50 changes: 50 additions & 0 deletions test/contracts/UsingDirectiveContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
pragma solidity ^0.8.13;

struct Data { mapping(uint => bool) flags; }
// Now we attach functions to the type.
// The attached functions can be used throughout the rest of the module.
// If you import the module, you have to
// repeat the using directive there, for example as
// import "flags.sol" as Flags;
// using {Flags.insert, Flags.remove, Flags.contains}
// for Flags.Data;
using {insert, remove, contains} for Data;

function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}

function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}

function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}


contract C {
Data knownValues;

function register(uint value) public {
// Here, all variables of type Data have
// corresponding member functions.
// The following function call is identical to
// `Set.insert(knownValues, value)`
require(knownValues.insert(value));
}
}
4 changes: 4 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,8 @@ describe('Solidity Merger', () => {
it('should compile file with user defined types at root level (0.8 support)', async () => {
await testFile('ContractWithUserDefinitionType');
});

it('should compile file with using directive at root level (0.8.13 support)', async () => {
await testFile('UsingDirectiveContract');
});
});

0 comments on commit fac2708

Please sign in to comment.