Commit 5681ff8b authored by aniket-engg's avatar aniket-engg Committed by Aniket

typo, type mismatch error, unit tests, types

parent 03508274
'use strict' 'use strict'
import { AstWalker } from 'remix-astwalker' import { AstWalker } from 'remix-astwalker'
import list from './modules/list' import list from './modules/list'
import { CompilationResult, AnalyzerModule, ReportObj } from 'types' import { CompilationResult, AnalyzerModule, AnalysisReportObj, AnalysisReport } from 'types'
type ModuleObj = { type ModuleObj = {
name: string name: string
mod: AnalyzerModule mod: AnalyzerModule
} }
interface AnalysisReportObj extends ReportObj {
error? : string
}
type AnalysisReport = {
name: string
report: AnalysisReportObj[]
}
export default class staticAnalysisRunner { export default class staticAnalysisRunner {
run (compilationResult: CompilationResult, toRun: any[], callback: ((reports: AnalysisReport[]) => void)): void { run (compilationResult: CompilationResult, toRun: any[], callback: ((reports: AnalysisReport[]) => void)): void {
......
...@@ -191,37 +191,13 @@ function getEffectedVariableName (effectNode: AssignmentAstNode | UnaryOperation ...@@ -191,37 +191,13 @@ function getEffectedVariableName (effectNode: AssignmentAstNode | UnaryOperation
} }
/** /**
* Finds first node of a certain type under a specific node.
* @node {AstNode} node to start form
* @type {String} Type the ast node should have
* @return {AstNode} null or node found
* Note: developed keeping identifier node search in mind to get first identifier node from left in subscope
*/
function findFirstSubNodeLTR (node: any, type: string): any {
if(node.nodeType && nodeType(node, type))
return node
else if(node.nodeType && nodeType(node, exactMatch('Assignment')))
return findFirstSubNodeLTR(node.leftHandSide, type)
else if(node.nodeType && nodeType(node, exactMatch('MemberAccess')))
return findFirstSubNodeLTR(node.expression, type)
else if(node.nodeType && nodeType(node, exactMatch('IndexAccess')))
return findFirstSubNodeLTR(node.baseExpression, type)
else if(node.nodeType && nodeType(node, exactMatch('UnaryOperation')))
return findFirstSubNodeLTR(node.subExpression, type)
}
/**
* Returns the identifier of a local call, Throws on wrong node. * Returns the identifier of a local call, Throws on wrong node.
* Example: f(103) => f * Example: f(103) => f
* @localCallNode {ASTNode} Function call node * @localCallNode {ASTNode} Function call node
* @return {string} name of the function called * @return {string} name of the function called
*/ */
function getLocalCallName (localCallNode: FunctionCallAstNode): string { function getLocalCallName (localCallNode: FunctionCallAstNode): string {
if (!isLocalCall(localCallNode) && !isAbiNamespaceCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not an local call Node') if (!isLocalCall(localCallNode) && !isAbiNamespaceCall(localCallNode)) throw new Error('staticAnalysisCommon.js: not a local call Node')
return localCallNode.expression.name return localCallNode.expression.name
} }
...@@ -232,7 +208,7 @@ function getLocalCallName (localCallNode: FunctionCallAstNode): string { ...@@ -232,7 +208,7 @@ function getLocalCallName (localCallNode: FunctionCallAstNode): string {
* @return {string} name of the function called * @return {string} name of the function called
*/ */
function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string { function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string {
if (!isThisLocalCall(thisLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an this local call Node') if (!isThisLocalCall(thisLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node')
return thisLocalCallNode.expression.memberName return thisLocalCallNode.expression.memberName
} }
...@@ -243,7 +219,7 @@ function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string { ...@@ -243,7 +219,7 @@ function getThisLocalCallName (thisLocalCallNode: FunctionCallAstNode): string {
* @return {string} name of the function called * @return {string} name of the function called
*/ */
function getSuperLocalCallName (superLocalCallNode: FunctionCallAstNode): string { function getSuperLocalCallName (superLocalCallNode: FunctionCallAstNode): string {
if (!isSuperLocalCall(superLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not an super local call Node') if (!isSuperLocalCall(superLocalCallNode.expression)) throw new Error('staticAnalysisCommon.js: not a super local call Node')
return superLocalCallNode.expression.memberName return superLocalCallNode.expression.memberName
} }
...@@ -271,7 +247,7 @@ function getExternalDirectCallContractName (extDirectCall: FunctionCallAstNode): ...@@ -271,7 +247,7 @@ function getExternalDirectCallContractName (extDirectCall: FunctionCallAstNode):
* @return {string} name of the contract the function is defined in * @return {string} name of the contract the function is defined in
*/ */
function getThisLocalCallContractName (thisLocalCall: FunctionCallAstNode): string { function getThisLocalCallContractName (thisLocalCall: FunctionCallAstNode): string {
if (!isThisLocalCall(thisLocalCall.expression)) throw new Error('staticAnalysisCommon.js: not an this local call Node') if (!isThisLocalCall(thisLocalCall.expression)) throw new Error('staticAnalysisCommon.js: not a this local call Node')
return thisLocalCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '') return thisLocalCall.expression.expression.typeDescriptions.typeString.replace(new RegExp(basicRegex.CONTRACTTYPE), '')
} }
...@@ -296,6 +272,7 @@ function getExternalDirectCallMemberName (extDirectCall: FunctionCallAstNode): s ...@@ -296,6 +272,7 @@ function getExternalDirectCallMemberName (extDirectCall: FunctionCallAstNode): s
* @return {string} name of a contract defined * @return {string} name of a contract defined
*/ */
function getContractName (contract: ContractDefinitionAstNode): string { function getContractName (contract: ContractDefinitionAstNode): string {
if (!nodeType(contract, exactMatch(nodeTypes.CONTRACTDEFINITION))) throw new Error('staticAnalysisCommon.js: not a ContractDefinition Node')
return contract.name return contract.name
} }
...@@ -307,6 +284,7 @@ function getContractName (contract: ContractDefinitionAstNode): string { ...@@ -307,6 +284,7 @@ function getContractName (contract: ContractDefinitionAstNode): string {
* @return {string} name of a function defined * @return {string} name of a function defined
*/ */
function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string { function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string {
if (!nodeType(funcDef, exactMatch(nodeTypes.FUNCTIONDEFINITION))) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition Node')
return funcDef.name return funcDef.name
} }
...@@ -318,6 +296,7 @@ function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string ...@@ -318,6 +296,7 @@ function getFunctionDefinitionName (funcDef: FunctionDefinitionAstNode): string
* @return {string} name of contract inherited from * @return {string} name of contract inherited from
*/ */
function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string { function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string {
if (!nodeType(inheritsNode, exactMatch(nodeTypes.INHERITANCESPECIFIER))) throw new Error('staticAnalysisCommon.js: not an InheritanceSpecifier Node')
return inheritsNode.baseName.name return inheritsNode.baseName.name
} }
...@@ -329,6 +308,7 @@ function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string ...@@ -329,6 +308,7 @@ function getInheritsFromName (inheritsNode: InheritanceSpecifierAstNode): string
* @return {string} variable name * @return {string} variable name
*/ */
function getDeclaredVariableName (varDeclNode: VariableDeclarationAstNode): string { function getDeclaredVariableName (varDeclNode: VariableDeclarationAstNode): string {
if (!nodeType(varDeclNode, exactMatch(nodeTypes.VARIABLEDECLARATION))) throw new Error('staticAnalysisCommon.js: not a VariableDeclaration Node')
return varDeclNode.name return varDeclNode.name
} }
...@@ -365,6 +345,7 @@ function getStateVariableDeclarationsFromContractNode (contractNode: ContractDef ...@@ -365,6 +345,7 @@ function getStateVariableDeclarationsFromContractNode (contractNode: ContractDef
* @return {parameterlist node} parameterlist node * @return {parameterlist node} parameterlist node
*/ */
function getFunctionOrModifierDefinitionParameterPart (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): ParameterListAstNode { function getFunctionOrModifierDefinitionParameterPart (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): ParameterListAstNode {
if (!nodeTypeIn(funcNode, [exactMatch(nodeTypes.FUNCTIONDEFINITION), exactMatch(nodeTypes.MODIFIERDEFINITION)])) throw new Error('staticAnalysisCommon.js: not a FunctionDefinition or ModifierDefinition Node')
return funcNode.parameters return funcNode.parameters
} }
...@@ -415,7 +396,7 @@ function getFunctionCallTypeParameterType (func: FunctionCallAstNode): string | ...@@ -415,7 +396,7 @@ function getFunctionCallTypeParameterType (func: FunctionCallAstNode): string |
* @return {string} name of the lib defined * @return {string} name of the lib defined
*/ */
function getLibraryCallContractName (node: FunctionCallAstNode): string | undefined { function getLibraryCallContractName (node: FunctionCallAstNode): string | undefined {
if (!isLibraryCall(node.expression)) throw new Error('staticAnalysisCommon.js: not an this library call Node') if (!isLibraryCall(node.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node')
const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.expression.typeDescriptions.typeString) const types: RegExpExecArray | null = new RegExp(basicRegex.LIBRARYTYPE).exec(node.expression.expression.typeDescriptions.typeString)
if(types) if(types)
return types[1] return types[1]
...@@ -432,7 +413,7 @@ function getLibraryCallContractName (node: FunctionCallAstNode): string | undefi ...@@ -432,7 +413,7 @@ function getLibraryCallContractName (node: FunctionCallAstNode): string | undefi
* @return {string} name of function called on the library * @return {string} name of function called on the library
*/ */
function getLibraryCallMemberName (funcCall: FunctionCallAstNode): string { function getLibraryCallMemberName (funcCall: FunctionCallAstNode): string {
// if (!isLibraryCall(funcCall)) throw new Error('staticAnalysisCommon.js: not an library call Node') if (!isLibraryCall(funcCall.expression)) throw new Error('staticAnalysisCommon.js: not a library call Node')
return funcCall.expression.memberName return funcCall.expression.memberName
} }
...@@ -1047,7 +1028,31 @@ function matches (...fnArgs: any[]): string { ...@@ -1047,7 +1028,31 @@ function matches (...fnArgs: any[]): string {
} }
/** /**
* Builds an function signature as used in the AST of the solc-json AST * Finds first node of a certain type under a specific node.
* @node {AstNode} node to start form
* @type {String} Type the ast node should have
* @return {AstNode} null or node found
* Note: developed keeping identifier node search in mind to get first identifier node from left in subscope
*/
function findFirstSubNodeLTR (node: any, type: string): any {
if(node.nodeType && nodeType(node, type))
return node
else if(node.nodeType && nodeType(node, exactMatch('Assignment')))
return findFirstSubNodeLTR(node.leftHandSide, type)
else if(node.nodeType && nodeType(node, exactMatch('MemberAccess')))
return findFirstSubNodeLTR(node.expression, type)
else if(node.nodeType && nodeType(node, exactMatch('IndexAccess')))
return findFirstSubNodeLTR(node.baseExpression, type)
else if(node.nodeType && nodeType(node, exactMatch('UnaryOperation')))
return findFirstSubNodeLTR(node.subExpression, type)
}
/**
* Builds a function signature as used in the AST of the solc-json AST
* @param {Array} paramTypes * @param {Array} paramTypes
* list of parameter type names * list of parameter type names
* @param {Array} returnTypes * @param {Array} returnTypes
......
...@@ -24,6 +24,15 @@ export interface ReportObj { ...@@ -24,6 +24,15 @@ export interface ReportObj {
more?: string more?: string
} }
export interface AnalysisReportObj extends ReportObj {
error? : string
}
export type AnalysisReport = {
name: string
report: AnalysisReportObj[]
}
export interface CompilationResult { export interface CompilationResult {
error?: CompilationError, error?: CompilationError,
/** not present if no errors/warnings were encountered */ /** not present if no errors/warnings were encountered */
......
...@@ -3,30 +3,30 @@ import { helpers } from 'remix-lib' ...@@ -3,30 +3,30 @@ import { helpers } from 'remix-lib'
import { readFileSync } from 'fs' import { readFileSync } from 'fs'
import { join } from 'path' import { join } from 'path'
import { default as StatRunner } from '../../dist/src/solidity-analyzer' import { default as StatRunner } from '../../dist/src/solidity-analyzer'
import { CompilationResult, AnalysisReportObj, AnalysisReport } from '../../src/types'
import { install, require as requireNPMmodule } from 'npm-install-version' import { install, require as requireNPMmodule } from 'npm-install-version'
install('solc@0.4.24') install('solc@0.4.24')
const compiler = requireNPMmodule('solc@0.4.24') const compiler = requireNPMmodule('solc@0.4.24')
const { compilerInput } = helpers.compiler const { compilerInput } = helpers.compiler
const folder = 'solidity-v0.4.24' const folder: string = 'solidity-v0.4.24'
function compile (fileName) { function compile (fileName: string): CompilationResult {
const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
return JSON.parse(compiler.compileStandardWrapper(compilerInput(content))) return JSON.parse(compiler.compileStandardWrapper(compilerInput(content)))
} }
test('staticAnalysisIssues.functionParameterPassingError', function (t) { test('staticAnalysisIssues.functionParameterPassingError', function (t) {
// https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474
t.plan(2) t.plan(2)
const res = compile('functionParameters.sol') const res: CompilationResult = compile('functionParameters.sol')
const Module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction').default const Module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction').default
const statRunner = new StatRunner() const statRunner: StatRunner = new StatRunner()
t.doesNotThrow(() => { t.doesNotThrow(() => {
statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports) => { statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports: AnalysisReport[]) => {})
})
}, 'Analysis should not throw') }, 'Analysis should not throw')
statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports) => { statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports: AnalysisReport[]) => {
t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
}) })
}) })
...@@ -3,30 +3,30 @@ import { helpers } from 'remix-lib' ...@@ -3,30 +3,30 @@ import { helpers } from 'remix-lib'
import { readFileSync } from 'fs' import { readFileSync } from 'fs'
import { join } from 'path' import { join } from 'path'
import { default as StatRunner } from '../../dist/src/solidity-analyzer' import { default as StatRunner } from '../../dist/src/solidity-analyzer'
import { CompilationResult, AnalysisReportObj, AnalysisReport } from '../../src/types'
import { install, require as requireNPMmodule } from 'npm-install-version' import { install, require as requireNPMmodule } from 'npm-install-version'
install('solc@0.5.0') install('solc@0.5.0')
const compiler = requireNPMmodule('solc@0.5.0') const compiler = requireNPMmodule('solc@0.5.0')
const {compilerInput } = helpers.compiler const {compilerInput } = helpers.compiler
const folder = 'solidity-v0.5' const folder: string = 'solidity-v0.5'
function compile (fileName) { function compile (fileName): CompilationResult {
const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') const content: string = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
return JSON.parse(compiler.compile(compilerInput(content))) return JSON.parse(compiler.compile(compilerInput(content)))
} }
test('staticAnalysisIssues.functionParameterPassingError', function (t) { test('staticAnalysisIssues.functionParameterPassingError', function (t) {
// https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474 // https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474
t.plan(2) t.plan(2)
const res = compile('functionParameters.sol') const res: CompilationResult = compile('functionParameters.sol')
const Module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction').default const Module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction').default
const statRunner = new StatRunner() const statRunner: StatRunner = new StatRunner()
t.doesNotThrow(() => { t.doesNotThrow(() => {
statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports) => { statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports: AnalysisReport[]) => {})
})
}, 'Analysis should not throw') }, 'Analysis should not throw')
statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports) => { statRunner.runWithModuleList(res, [{ name: new Module().name, mod: new Module() }], (reports: AnalysisReport[]) => {
t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) t.ok(!reports.some((mod: AnalysisReport) => mod.report.some((rep: AnalysisReportObj) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
}) })
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment