Skip to content

Commit

Permalink
feat(comments): add global comments as exports
Browse files Browse the repository at this point in the history
  • Loading branch information
RyuuGan committed May 17, 2020
1 parent dc7e2a0 commit bf0b6e4
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 8 deletions.
70 changes: 68 additions & 2 deletions lib/antlr/visitors/exportVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CharStreams, CommonTokenStream } from 'antlr4ts';
import { CharStreams, CommonTokenStream, Token } from 'antlr4ts';
import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker';
import { SolidityLexer } from '../generated/SolidityLexer';
import { SolidityListener } from '../generated/SolidityListener';
Expand All @@ -12,9 +12,12 @@ import {
} from '../generated/SolidityParser';
import { ExportType, ExportVisitResult, VisitCallback } from './types';

const HIDDEN_CHANNEL = 1;

export class SolidityExportVisitor {
#inputContent: string;
#antlrTree: SourceUnitContext;
#comments: Token[] = [];
constructor(inputContent: string) {
this.#inputContent = inputContent;

Expand All @@ -23,11 +26,74 @@ export class SolidityExportVisitor {
const tokens = new CommonTokenStream(lexer);
const parser = new SolidityParser(tokens);
this.#antlrTree = parser.sourceUnit();

this.#comments = tokens
.getRange(0, tokens.size)
.filter((t) => t.channel === HIDDEN_CHANNEL);
}

visit(onVisit: VisitCallback<ExportVisitResult>) {
const listener: SolidityListener = new ExportVisitor(onVisit);
const listener: SolidityListener = new ExportVisitor((visitResult) => {
this.onVisit(visitResult, onVisit);
});
ParseTreeWalker.DEFAULT.walk(listener, this.#antlrTree);
this.flushComments(onVisit);
}

private onVisit(
visitResult: ExportVisitResult,
onVisit: VisitCallback<ExportVisitResult>,
) {
if (!this.#comments.length) {
return onVisit(visitResult);
}
this.emitCommentsBefore(visitResult, onVisit);
onVisit(visitResult);
}

private emitCommentsBefore(
visitResult: ExportVisitResult,
onVisit: VisitCallback<ExportVisitResult>,
) {
while (
this.#comments.length &&
this.#comments[0].startIndex < visitResult.start
) {
const comment = this.#comments.shift();
if (!comment) {
continue;
}
onVisit(this.buildComment(comment));
}
while (
this.#comments.length &&
this.#comments[0].stopIndex < visitResult.end
) {
const skipped = this.#comments.shift();
}
}

private flushComments(onVisit: VisitCallback<ExportVisitResult>) {
if (!this.#comments.length) {
return;
}

this.#comments.forEach((comment) => onVisit(this.buildComment(comment)));
}

private buildComment(comment: Token): ExportVisitResult {
return {
abstract: false,
body: {
start: comment.startIndex,
end: comment.stopIndex,
},
start: comment.startIndex,
end: comment.stopIndex,
is: null,
name: `Comment#${comment.startIndex}`,
type: 'comment',
};
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/antlr/visitors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface ImportVisitNamedImport {
as: string | null
}

export type ExportType = 'contract' | 'library' | 'interface' | 'struct' | 'enum';
export type ExportType = 'contract' | 'library' | 'interface' | 'struct' | 'enum' | 'comment';

export interface ExportVisitResult extends RangeVisitResult {
abstract: boolean;
Expand Down
2 changes: 1 addition & 1 deletion lib/exportsAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SolidityExportVisitor } from './antlr/visitors/exportVisitor';
const error = Debug('sol-merger:error');

export interface ExportsAnalyzerResult {
type: 'contract' | 'library' | 'interface' | 'struct' | 'enum';
type: 'contract' | 'library' | 'interface' | 'struct' | 'enum' | 'comment';
name: string;
is: string;
body: string;
Expand Down
4 changes: 4 additions & 0 deletions lib/fileAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class FileAnalyzer {
newName: string | null,
globalRenames: RegistredImport[],
): string {
if (e.type === 'comment') {
return e.body;
}

let is = e.is;
if (is) {
globalRenames.forEach((i) => {
Expand Down
11 changes: 9 additions & 2 deletions lib/merger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ export class Merger {
await this.init(file);
}
if (this.importRegistry.isImportProcessed(parentImport?.importStatement)) {
debug(' %s Import statement already processed: %s', '⚠', parentImport?.importStatement);
debug(
' %s Import statement already processed: %s',
'⚠',
parentImport?.importStatement,
);
return '';
}
if (parentImport) {
Expand Down Expand Up @@ -150,6 +154,9 @@ export class Merger {
);

const shouldBeImported = (exportName: string) => {
if (e.type === 'comment' && (isAllImport || isRenameGlobalImport)) {
return true;
}
return (
isAllImport ||
isRenameGlobalImport ||
Expand All @@ -169,7 +176,7 @@ export class Merger {
analyzedFile.filename,
e.name,
rename,
);
)
if (isImported) {
debug('%s Already imported: %s %s', '⚠', e.name, analyzedFile.filename);
return [];
Expand Down
2 changes: 2 additions & 0 deletions test/compiled/ContactWithKeywordsInsideString.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ contract ConflictingInheritance {

}

// This contract will not be detected

contract B {
uint private b;

Expand Down
21 changes: 21 additions & 0 deletions test/compiled/GlobalComments.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity 0.6.0;


/*
* Multiline Comment Before
*/

// This is not included Before

contract MyContract {
// This is included
function myFunction() {
// This is included
}
}

// This is not included After

/*
* Multiline Comment After
*/
2 changes: 1 addition & 1 deletion test/contracts/Enum.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.4.0;

enum State1 { Created1, Locked1, Inactive1 } // Enum
enum State1 { Created1, Locked1, Inactive1 }

contract Purchase {
enum State { Created, Locked, Inactive } // Enum
Expand Down
16 changes: 16 additions & 0 deletions test/contracts/GlobalComments.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity 0.6.0;

/*
* Multiline Comment Before
*/
// This is not included Before
contract MyContract {
// This is included
function myFunction() {
// This is included
}
}
// This is not included After
/*
* Multiline Comment After
*/
24 changes: 23 additions & 1 deletion test/exportsAnalyzer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,36 @@ describe('ExportsAnalyzer', () => {
]);
});

it('should return empty array if there are no exports', () => {
it('should return comments array', () => {
const exportsAnalyzer = new ExportsAnalyzer(`
// Some contracts without exports
// Some contract text that is not required here
`);
const exports = exportsAnalyzer.analyzeExports();

assert.deepEqual(exports, [
{
body: '// Some contracts without exports',
is: '',
name: 'Comment#9',
type: 'comment',
},
{
body: '// Some contract text that is not required here',
is: '',
name: 'Comment#52',
type: 'comment',
},
]);
});

it('should return empty array if there are no exports', () => {
const exportsAnalyzer = new ExportsAnalyzer(`
`);
const exports = exportsAnalyzer.analyzeExports();

assert.deepEqual(exports, []);
});
});
Expand Down
4 changes: 4 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ describe('Solidity Merger', () => {
await testFile('ImportStruct');
});

it('should compile while when importing the struct', async () => {
await testFile('GlobalComments');
});

it('should compile file without imports and exports (empty content)', async () => {
const merger = new Merger();
const file = path.join(__dirname, `/contracts/EmptyFile.sol`);
Expand Down

0 comments on commit bf0b6e4

Please sign in to comment.