modules updated for current AST

pull/7/head
aniket-engg 5 years ago committed by Aniket
parent b6b07a0c71
commit 0569590c5f
  1. 2
      remix-analyzer/src/solidity-analyzer/index.ts
  2. 52
      remix-analyzer/src/solidity-analyzer/modules/abstractAstView.ts
  3. 32
      remix-analyzer/src/solidity-analyzer/modules/checksEffectsInteraction.ts
  4. 40
      remix-analyzer/src/solidity-analyzer/modules/constantFunctions.ts
  5. 35
      remix-analyzer/src/solidity-analyzer/modules/functionCallGraph.ts
  6. 2
      remix-analyzer/src/solidity-analyzer/modules/gasCosts.ts
  7. 32
      remix-analyzer/src/solidity-analyzer/modules/noReturn.ts
  8. 12
      remix-analyzer/src/solidity-analyzer/modules/selfdestruct.ts
  9. 22
      remix-analyzer/src/solidity-analyzer/modules/similarVariableNames.ts
  10. 2
      remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.ts
  11. 43
      remix-analyzer/src/types.ts
  12. 848
      remix-analyzer/test/analysis/staticAnalysisCommon-test.ts
  13. 1758
      remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts
  14. 1844
      remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.ts
  15. 56
      remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.ts
  16. 56
      remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.ts

@ -18,7 +18,7 @@ export default class staticAnalysisRunner {
// Also provide convenience analysis via the AST walker. // Also provide convenience analysis via the AST walker.
const walker = new AstWalker() const walker = new AstWalker()
for (let k in compilationResult.sources) { for (let k in compilationResult.sources) {
walker.walk(compilationResult.sources[k].legacyAST, {'*': (node) => { walker.walk(compilationResult.sources[k].AST, {'*': (node) => {
modules.map((item, i) => { modules.map((item, i) => {
if (item.mod.visit !== undefined) { if (item.mod.visit !== undefined) {
try { try {

@ -3,14 +3,14 @@ import { getStateVariableDeclarationsFormContractNode,
getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName, getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName,
getFunctionDefinitionReturnParameterPart } from './staticAnalysisCommon' getFunctionDefinitionReturnParameterPart } from './staticAnalysisCommon'
import { AstWalker } from 'remix-astwalker' import { AstWalker } from 'remix-astwalker'
import { CommonAstNode, FunctionDefinitionAstNode, ParameterListAstNode, ModifierDefinitionAstNode } from 'types' import { CommonAstNode, FunctionDefinitionAstNode, ParameterListAstNode, ModifierDefinitionAstNode, ContractHLAst, VariableDeclarationStatementAstNode, VariableDeclarationAstNode, FunctionHLAst } from 'types'
export default class abstractAstView { export default class abstractAstView {
contracts = [] contracts: ContractHLAst[] = []
currentContractIndex = null currentContractIndex: number = -1
currentFunctionIndex = null currentFunctionIndex: number = -1
currentModifierIndex = null currentModifierIndex: number = -1
isFunctionNotModifier = false isFunctionNotModifier: boolean = false
/* /*
file1: contract c{} file1: contract c{}
file2: import "file1" as x; contract c{} file2: import "file1" as x; contract c{}
@ -19,7 +19,7 @@ export default class abstractAstView {
Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to
fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c
*/ */
multipleContractsWithSameName = false multipleContractsWithSameName: boolean = false
/** /**
@ -47,7 +47,7 @@ export default class abstractAstView {
* @contractsOut {list} return list for high level AST view * @contractsOut {list} return list for high level AST view
* @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis. * @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis.
*/ */
build_visit (relevantNodeFilter) { build_visit (relevantNodeFilter: Function): Function {
var that = this var that = this
return function (node: any) { return function (node: any) {
if (node.nodeType === "ContractDefinition") { if (node.nodeType === "ContractDefinition") {
@ -89,34 +89,34 @@ export default class abstractAstView {
if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.') if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.')
that.getCurrentFunction(that).modifierInvocations.push(node) that.getCurrentFunction(that).modifierInvocations.push(node)
} else if (relevantNodeFilter(node)) { } else if (relevantNodeFilter(node)) {
let scope = (that.isFunctionNotModifier) ? that.getCurrentFunction(that) : that.getCurrentModifier(that) let scope: any = (that.isFunctionNotModifier) ? that.getCurrentFunction(that) : that.getCurrentModifier(that)
if (scope) { if (scope) {
scope.relevantNodes.push(node) scope.relevantNodes.push(node)
} else { } else {
scope = that.getCurrentContract(that) // if we are not in a function scope, add the node to the contract scope scope = that.getCurrentContract(that) // if we are not in a function scope, add the node to the contract scope
if (scope && node.children[0] && node.children[0].attributes && node.children[0].attributes.referencedDeclaration) { if (scope && node.referencedDeclaration) {
scope.relevantNodes.push({ referencedDeclaration: node.children[0].attributes.referencedDeclaration, node: node }) scope.relevantNodes.push({ referencedDeclaration: node.referencedDeclaration, node: node })
} }
} }
} }
} }
} }
build_report (wrap) { build_report (wrap: Function): Function {
var that = this const that: abstractAstView = this
return function (compilationResult) { return function (compilationResult) {
that.resolveStateVariablesInHierarchy(that.contracts) that.resolveStateVariablesInHierarchy(that.contracts)
return wrap(that.contracts, that.multipleContractsWithSameName) return wrap(that.contracts, that.multipleContractsWithSameName)
} }
} }
private resolveStateVariablesInHierarchy (contracts) { private resolveStateVariablesInHierarchy (contracts: ContractHLAst[]): void {
contracts.map((c) => { contracts.map((c) => {
this.resolveStateVariablesInHierarchyForContract(c, contracts) this.resolveStateVariablesInHierarchyForContract(c, contracts)
}) })
} }
private resolveStateVariablesInHierarchyForContract (currentContract, contracts) { private resolveStateVariablesInHierarchyForContract (currentContract: ContractHLAst, contracts: ContractHLAst[]): void {
currentContract.inheritsFrom.map((inheritsFromName) => { currentContract.inheritsFrom.map((inheritsFromName) => {
// add variables from inherited contracts // add variables from inherited contracts
const inheritsFrom = contracts.find((contract) => getContractName(contract.node) === inheritsFromName) const inheritsFrom = contracts.find((contract) => getContractName(contract.node) === inheritsFromName)
@ -128,8 +128,8 @@ export default class abstractAstView {
}) })
} }
private setCurrentContract (that, contract) { private setCurrentContract (that: abstractAstView, contract: ContractHLAst): void {
const name = getContractName(contract.node) const name: string = getContractName(contract.node)
if (that.contracts.map((c) => getContractName(c.node)).filter((n) => n === name).length > 0) { if (that.contracts.map((c) => getContractName(c.node)).filter((n) => n === name).length > 0) {
console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment') console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment')
that.multipleContractsWithSameName = true that.multipleContractsWithSameName = true
@ -137,33 +137,33 @@ export default class abstractAstView {
that.currentContractIndex = (that.contracts.push(contract) - 1) that.currentContractIndex = (that.contracts.push(contract) - 1)
} }
private setCurrentFunction (that, func) { private setCurrentFunction (that: abstractAstView, func: FunctionHLAst): void {
that.isFunctionNotModifier = true that.isFunctionNotModifier = true
that.currentFunctionIndex = (that.getCurrentContract(that).functions.push(func) - 1) that.currentFunctionIndex = (that.getCurrentContract(that).functions.push(func) - 1)
} }
private setCurrentModifier (that, modi) { private setCurrentModifier (that, modi): void {
that.isFunctionNotModifier = false that.isFunctionNotModifier = false
that.currentModifierIndex = (that.getCurrentContract(that).modifiers.push(modi) - 1) that.currentModifierIndex = (that.getCurrentContract(that).modifiers.push(modi) - 1)
} }
private getCurrentContract (that) { private getCurrentContract (that: abstractAstView): ContractHLAst {
return that.contracts[that.currentContractIndex] return that.contracts[that.currentContractIndex]
} }
private getCurrentFunction (that) { private getCurrentFunction (that: abstractAstView): FunctionHLAst {
return that.getCurrentContract(that).functions[that.currentFunctionIndex] return that.getCurrentContract(that).functions[that.currentFunctionIndex]
} }
private getCurrentModifier (that) { private getCurrentModifier (that:abstractAstView) {
return that.getCurrentContract(that).modifiers[that.currentModifierIndex] return that.getCurrentContract(that).modifiers[that.currentModifierIndex]
} }
private getLocalParameters (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode) { private getLocalParameters (funcNode: FunctionDefinitionAstNode | ModifierDefinitionAstNode): string[] {
return getFunctionOrModifierDefinitionParameterPart(funcNode).parameters.map(getType) return getFunctionOrModifierDefinitionParameterPart(funcNode).parameters.map(getType)
} }
private getReturnParameters (funcNode: FunctionDefinitionAstNode) { private getReturnParameters (funcNode: FunctionDefinitionAstNode): Record<string, string>[] {
return this.getLocalVariables(getFunctionDefinitionReturnParameterPart(funcNode)).map((n) => { return this.getLocalVariables(getFunctionDefinitionReturnParameterPart(funcNode)).map((n) => {
return { return {
type: getType(n), type: getType(n),
@ -172,8 +172,8 @@ export default class abstractAstView {
}) })
} }
private getLocalVariables (funcNode: ParameterListAstNode) { private getLocalVariables (funcNode: ParameterListAstNode): VariableDeclarationAstNode[] {
const locals: any[] = [] const locals: VariableDeclarationAstNode[] = []
new AstWalker().walk(funcNode, {'*': function (node) { new AstWalker().walk(funcNode, {'*': function (node) {
if (node.nodeType === "VariableDeclaration") locals.push(node) if (node.nodeType === "VariableDeclaration") locals.push(node)
return true return true

@ -4,7 +4,7 @@ import { isInteraction, isEffect, isLocalCallGraphRelevantNode, getFullQuallyfie
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph' import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph'
import AbstractAst from './abstractAstView' import AbstractAst from './abstractAstView'
import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, AstNodeLegacy, CompilationResult} from './../../types' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, VariableDeclarationAstNode, FunctionHLAst, ContractCallGraph, Context} from './../../types'
export default class checksEffectsInteraction implements AnalyzerModule { export default class checksEffectsInteraction implements AnalyzerModule {
name: string = 'Check effects: ' name: string = 'Check effects: '
@ -14,17 +14,17 @@ export default class checksEffectsInteraction implements AnalyzerModule {
abstractAst: AbstractAst = new AbstractAst() abstractAst: AbstractAst = new AbstractAst()
visit = this.abstractAst.build_visit((node: AstNodeLegacy) => isInteraction(node) || isEffect(node) || isLocalCallGraphRelevantNode(node)) visit: Function = this.abstractAst.build_visit((node: any) => isInteraction(node) || isEffect(node) || isLocalCallGraphRelevantNode(node))
report = this.abstractAst.build_report(this._report.bind(this)) report: Function = this.abstractAst.build_report(this._report.bind(this))
private _report (contracts, multipleContractsWithSameName): ReportObj[] { private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] {
const warnings: ReportObj[] = [] const warnings: ReportObj[] = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0) const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0)
const callGraph = buildGlobalFuncCallGraph(contracts) const callGraph: Record<string, ContractCallGraph> = buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
func.changesState = this.checkIfChangesState( func['changesState'] = this.checkIfChangesState(
getFullQuallyfiedFuncDefinitionIdent( getFullQuallyfiedFuncDefinitionIdent(
contract.node, contract.node,
func.node, func.node,
@ -43,7 +43,7 @@ export default class checksEffectsInteraction implements AnalyzerModule {
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
warnings.push({ warnings.push({
warning: `Potential Violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`, warning: `Potential Violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`,
location: func.src, location: func['src'],
more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy' more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy'
}) })
} }
@ -52,17 +52,17 @@ export default class checksEffectsInteraction implements AnalyzerModule {
return warnings return warnings
} }
private getContext (callGraph, currentContract, func) { private getContext (callGraph: Record<string, ContractCallGraph>, currentContract: ContractHLAst, func: FunctionHLAst): Context {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) }
} }
private getStateVariables (contract, func) { private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] {
return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration))
} }
private isPotentialVulnerableFunction (func, context) { private isPotentialVulnerableFunction (func: FunctionHLAst, context: Context): boolean {
let isPotentialVulnerable = false let isPotentialVulnerable: boolean = false
let interaction = false let interaction: boolean = false
func.relevantNodes.forEach((node) => { func.relevantNodes.forEach((node) => {
if (isInteraction(node)) { if (isInteraction(node)) {
interaction = true interaction = true
@ -73,15 +73,15 @@ export default class checksEffectsInteraction implements AnalyzerModule {
return isPotentialVulnerable return isPotentialVulnerable
} }
private isLocalCallWithStateChange (node, context) { private isLocalCallWithStateChange (node: any, context: Context): boolean {
if (isLocalCallGraphRelevantNode(node)) { if (isLocalCallGraphRelevantNode(node)) {
const func = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) const func = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node))
return !func || (func && func.node.changesState) return !func || (func && func.node['changesState'])
} }
return false return false
} }
private checkIfChangesState (startFuncName, context) { private checkIfChangesState (startFuncName: string, context: Context): boolean {
return analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => isWriteOnStateVariable(node, context.stateVariables)) return analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => isWriteOnStateVariable(node, context.stateVariables))
} }
} }

@ -6,7 +6,7 @@ import { isLowLevelCall, isTransfer, isExternalDirectCall, isEffect, isLocalCall
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph' import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph'
import AbstractAst from './abstractAstView' import AbstractAst from './abstractAstView'
import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, AstNodeLegacy, CompilationResult, CommonAstNode} from './../../types' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractCallGraph, Context, ContractHLAst, FunctionHLAst, VariableDeclarationAstNode, FunctionCallGraph} from './../../types'
export default class constantFunctions implements AnalyzerModule { export default class constantFunctions implements AnalyzerModule {
name: string = 'Constant functions: ' name: string = 'Constant functions: '
@ -16,7 +16,7 @@ export default class constantFunctions implements AnalyzerModule {
abstractAst: AbstractAst = new AbstractAst() abstractAst: AbstractAst = new AbstractAst()
visit = this.abstractAst.build_visit( visit: Function = this.abstractAst.build_visit(
(node: any) => isLowLevelCall(node) || (node: any) => isLowLevelCall(node) ||
isTransfer(node) || isTransfer(node) ||
isExternalDirectCall(node) || isExternalDirectCall(node) ||
@ -28,20 +28,20 @@ export default class constantFunctions implements AnalyzerModule {
isDeleteUnaryOperation(node) isDeleteUnaryOperation(node)
) )
report = this.abstractAst.build_report(this._report.bind(this)) report: Function = this.abstractAst.build_report(this._report.bind(this))
private _report (contracts, multipleContractsWithSameName): ReportObj[] { private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] {
const warnings: ReportObj[] = [] const warnings: ReportObj[] = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0) const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0)
const callGraph = buildGlobalFuncCallGraph(contracts) const callGraph: Record<string, ContractCallGraph> = buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
if (isPayableFunction(func.node) || isConstructor(func.node)) { if (isPayableFunction(func.node) || isConstructor(func.node)) {
func.potentiallyshouldBeConst = false func['potentiallyshouldBeConst'] = false
} else { } else {
func.potentiallyshouldBeConst = this.checkIfShouldBeConstant( func['potentiallyshouldBeConst'] = this.checkIfShouldBeConstant(
getFullQuallyfiedFuncDefinitionIdent( getFullQuallyfiedFuncDefinitionIdent(
contract.node, contract.node,
func.node, func.node,
@ -57,20 +57,20 @@ export default class constantFunctions implements AnalyzerModule {
}) })
contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => { contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => {
if (isConstantFunction(func.node) !== func.potentiallyshouldBeConst) { if (isConstantFunction(func.node) !== func['potentiallyshouldBeConst']) {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : '' let comments: string = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : '' comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
if (func.potentiallyshouldBeConst) { if (func['potentiallyshouldBeConst']) {
warnings.push({ warnings.push({
warning: `${funcName} : Potentially should be constant but is not. ${comments}`, warning: `${funcName} : Potentially should be constant but is not. ${comments}`,
location: func.src, location: func['src'],
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions' more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
}) })
} else { } else {
warnings.push({ warnings.push({
warning: `${funcName} : Is constant but potentially should not be. ${comments}`, warning: `${funcName} : Is constant but potentially should not be. ${comments}`,
location: func.src, location: func['src'],
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions' more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
}) })
} }
@ -80,19 +80,19 @@ export default class constantFunctions implements AnalyzerModule {
return warnings return warnings
} }
private getContext (callGraph, currentContract, func) { private getContext (callGraph: Record<string, ContractCallGraph>, currentContract: ContractHLAst, func: FunctionHLAst): Context {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) } return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) }
} }
private getStateVariables (contract, func) { private getStateVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] {
return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration)) return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration))
} }
private checkIfShouldBeConstant (startFuncName, context) { private checkIfShouldBeConstant (startFuncName: string, context): boolean {
return !analyseCallGraph(context.callGraph, startFuncName, context, this.isConstBreaker.bind(this)) return !analyseCallGraph(context.callGraph, startFuncName, context, this.isConstBreaker.bind(this))
} }
private isConstBreaker (node, context) { private isConstBreaker (node: any, context: Context): boolean {
return isWriteOnStateVariable(node, context.stateVariables) || return isWriteOnStateVariable(node, context.stateVariables) ||
isLowLevelCall(node) || isLowLevelCall(node) ||
isTransfer(node) || isTransfer(node) ||
@ -104,9 +104,9 @@ export default class constantFunctions implements AnalyzerModule {
isDeleteUnaryOperation(node) isDeleteUnaryOperation(node)
} }
private isCallOnNonConstExternalInterfaceFunction (node, context) { private isCallOnNonConstExternalInterfaceFunction (node: any, context: Context): boolean {
if (isExternalDirectCall(node)) { if (isExternalDirectCall(node)) {
const func = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract, node)) const func: FunctionCallGraph | undefined = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node))
return !func || (func && !isConstantFunction(func.node.node)) return !func || (func && !isConstantFunction(func.node.node))
} }
return false return false

@ -1,9 +1,11 @@
'use strict' 'use strict'
import { FunctionHLAst, ContractHLAst, FunctionCallGraph, ContractCallGraph } from "types"
const common = require('./staticAnalysisCommon') const common = require('./staticAnalysisCommon')
function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIdent, extractFuncDefIdent) { function buildLocalFuncCallGraphInternal (functions: FunctionHLAst[], nodeFilter: any , extractNodeIdent: any, extractFuncDefIdent: Function): Record<string, FunctionCallGraph> {
const callGraph = {} const callGraph: Record<string, FunctionCallGraph> = {}
functions.forEach((func) => { functions.forEach((func) => {
const calls = func.relevantNodes const calls = func.relevantNodes
.filter(nodeFilter) .filter(nodeFilter)
@ -39,12 +41,12 @@ function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIden
* @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js * @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js
* @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph * @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph
*/ */
export function buildGlobalFuncCallGraph (contracts) { export function buildGlobalFuncCallGraph (contracts: ContractHLAst[]): Record<string, ContractCallGraph> {
const callGraph = {} const callGraph: Record<string, ContractCallGraph> = {}
contracts.forEach((contract) => { contracts.forEach((contract) => {
const filterNodes = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) } const filterNodes: Function = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) }
const getNodeIdent = (node) => { return common.getFullQualifiedFunctionCallIdent(contract.node, node) } const getNodeIdent: Function = (node) => { return common.getFullQualifiedFunctionCallIdent(contract.node, node) }
const getFunDefIdent = (funcDef) => { return common.getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) } const getFunDefIdent: Function = (funcDef) => { return common.getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) }
callGraph[common.getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) } callGraph[common.getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) }
}) })
@ -60,12 +62,12 @@ export function buildGlobalFuncCallGraph (contracts) {
* @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph * @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph
* @return {bool} returns map from contract name to contract call graph * @return {bool} returns map from contract name to contract call graph
*/ */
export function analyseCallGraph (callGraph, funcName, context, nodeCheck) { export function analyseCallGraph (callGraph: Record<string, ContractCallGraph>, funcName: string, context: object, nodeCheck): boolean {
return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {}) return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {})
} }
function analyseCallGraphInternal (callGraph, funcName, context, combinator, nodeCheck, visited) { function analyseCallGraphInternal (callGraph: Record<string, ContractCallGraph>, funcName: string, context: object, combinator: Function, nodeCheck, visited : object): boolean {
const current = resolveCallGraphSymbol(callGraph, funcName) const current: FunctionCallGraph | undefined = resolveCallGraphSymbol(callGraph, funcName)
if (current === undefined || visited[funcName] === true) return true if (current === undefined || visited[funcName] === true) return true
visited[funcName] = true visited[funcName] = true
@ -74,23 +76,23 @@ function analyseCallGraphInternal (callGraph, funcName, context, combinator, nod
current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false)) current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false))
} }
export function resolveCallGraphSymbol (callGraph, funcName) { export function resolveCallGraphSymbol (callGraph: Record<string, ContractCallGraph>, funcName: string): FunctionCallGraph | undefined {
return resolveCallGraphSymbolInternal(callGraph, funcName, false) return resolveCallGraphSymbolInternal(callGraph, funcName, false)
} }
function resolveCallGraphSymbolInternal (callGraph, funcName, silent) { function resolveCallGraphSymbolInternal (callGraph: Record<string, ContractCallGraph>, funcName: string, silent: boolean): FunctionCallGraph | undefined {
let current let current: FunctionCallGraph | null = null
if (funcName.includes('.')) { if (funcName.includes('.')) {
const parts = funcName.split('.') const parts = funcName.split('.')
const contractPart = parts[0] const contractPart = parts[0]
const functionPart = parts[1] const functionPart = parts[1]
const currentContract = callGraph[contractPart] const currentContract: ContractCallGraph = callGraph[contractPart]
if (!(currentContract === undefined)) { if (!(currentContract === undefined)) {
current = currentContract.functions[funcName] current = currentContract.functions[funcName]
// resolve inheritance hierarchy // resolve inheritance hierarchy
if (current === undefined) { if (current === undefined) {
// resolve inheritance lookup in linearized fashion // resolve inheritance lookup in linearized fashion
const inheritsFromNames = currentContract.contract.inheritsFrom.reverse() const inheritsFromNames: string[] = currentContract.contract.inheritsFrom.reverse()
for (let i = 0; i < inheritsFromNames.length; i++) { for (let i = 0; i < inheritsFromNames.length; i++) {
const res = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true) const res = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true)
if (!(res === undefined)) return res if (!(res === undefined)) return res
@ -103,5 +105,6 @@ function resolveCallGraphSymbolInternal (callGraph, funcName, silent) {
throw new Error('functionCallGraph.js: function does not have full qualified name.') throw new Error('functionCallGraph.js: function does not have full qualified name.')
} }
if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`) if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`)
return current if(current !== null)
return current
} }

@ -15,7 +15,7 @@ export default class gasCosts {
* @param {Function} cb - callback * @param {Function} cb - callback
*/ */
// @TODO has been copied from remix-ide repo ! should fix that soon ! // @TODO has been copied from remix-ide repo ! should fix that soon !
visitContracts (contracts, cb) { visitContracts (contracts, cb: Function) {
for (let file in contracts) { for (let file in contracts) {
for (let name in contracts[file]) { for (let name in contracts[file]) {
if (cb({ name: name, object: contracts[file][name], file: file })) return if (cb({ name: name, object: contracts[file][name], file: file })) return

@ -2,7 +2,7 @@ import { default as category } from './categories'
import { hasFunctionBody, getFullQuallyfiedFuncDefinitionIdent, getEffectedVariableName } from './staticAnalysisCommon' import { hasFunctionBody, getFullQuallyfiedFuncDefinitionIdent, getEffectedVariableName } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView' import AbstractAst from './abstractAstView'
import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CommonAstNode, FunctionDefinitionAstNode} from './../../types' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CommonAstNode, FunctionDefinitionAstNode, ContractHLAst, FunctionHLAst} from './../../types'
export default class noReturn implements AnalyzerModule { export default class noReturn implements AnalyzerModule {
name: string = 'no return: ' name: string = 'no return: '
@ -12,26 +12,26 @@ export default class noReturn implements AnalyzerModule {
abstractAst: AbstractAst = new AbstractAst() abstractAst: AbstractAst = new AbstractAst()
visit = this.abstractAst.build_visit( visit: Function = this.abstractAst.build_visit(
(node: CommonAstNode) => node.nodeType === "Return" || node.nodeType === "Assignment" (node: CommonAstNode) => node.nodeType === "Return" || node.nodeType === "Assignment"
) )
report = this.abstractAst.build_report(this._report.bind(this)) report: Function = this.abstractAst.build_report(this._report.bind(this))
private _report (contracts, multipleContractsWithSameName): ReportObj[] { private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] {
const warnings: any[] = [] const warnings: ReportObj[] = []
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => { contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
if (this.hasNamedAndUnnamedReturns(func)) { if (this.hasNamedAndUnnamedReturns(func)) {
warnings.push({ warnings.push({
warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`, warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`,
location: func.src location: func['src']
}) })
} else if (this.shouldReturn(func) && !(this.hasReturnStatement(func) || (this.hasNamedReturns(func) && this.hasAssignToAllNamedReturns(func)))) { } else if (this.shouldReturn(func) && !(this.hasReturnStatement(func) || (this.hasNamedReturns(func) && this.hasAssignToAllNamedReturns(func)))) {
warnings.push({ warnings.push({
warning: `${funcName}: Defines a return type but never explicitly returns a value.`, warning: `${funcName}: Defines a return type but never explicitly returns a value.`,
location: func.src location: func['src']
}) })
} }
}) })
@ -40,26 +40,26 @@ export default class noReturn implements AnalyzerModule {
return warnings return warnings
} }
private shouldReturn (func): boolean { private shouldReturn (func: FunctionHLAst): boolean {
return func.returns.length > 0 return func.returns.length > 0
} }
private hasReturnStatement (func: CommonAstNode): boolean { private hasReturnStatement (func: FunctionHLAst): boolean {
return func.relevantNodes.filter(n => n.nodeType === "Return").length > 0 return func.relevantNodes.filter(n => n.nodeType === "Return").length > 0
} }
private hasAssignToAllNamedReturns (func): boolean { private hasAssignToAllNamedReturns (func: FunctionHLAst): boolean {
const namedReturns = func.returns.filter((n) => n.name.length > 0).map((n) => n.name) const namedReturns: string[] = func.returns.filter((n) => n.name.length > 0).map((n) => n.name)
const assignedVars = func.relevantNodes.filter(n => n.nodeType === "Assignment").map(getEffectedVariableName) const assignedVars: string[] = func.relevantNodes.filter(n => n.nodeType === "Assignment").map(getEffectedVariableName)
const diff = namedReturns.filter(e => !assignedVars.includes(e)) const diff: string[] = namedReturns.filter(e => !assignedVars.includes(e))
return diff.length === 0 return diff.length === 0
} }
private hasNamedReturns (func): boolean { private hasNamedReturns (func: FunctionHLAst): boolean {
return func.returns.filter((n) => n.name.length > 0).length > 0 return func.returns.filter((n) => n.name.length > 0).length > 0
} }
private hasNamedAndUnnamedReturns (func): boolean { private hasNamedAndUnnamedReturns (func: FunctionHLAst): boolean {
return func.returns.filter((n) => n.name.length === 0).length > 0 && return func.returns.filter((n) => n.name.length === 0).length > 0 &&
this.hasNamedReturns(func) this.hasNamedReturns(func)
} }

@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isStatement, isSelfdestructCall } from './staticAnalysisCommon' import { isStatement, isSelfdestructCall } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView' import AbstractAst from './abstractAstView'
import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult} from './../../types' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst} from './../../types'
export default class selfdestruct implements AnalyzerModule { export default class selfdestruct implements AnalyzerModule {
name: string = 'Selfdestruct: ' name: string = 'Selfdestruct: '
@ -10,19 +10,19 @@ export default class selfdestruct implements AnalyzerModule {
category: ModuleCategory = category.SECURITY category: ModuleCategory = category.SECURITY
algorithm: ModuleAlgorithm = algorithm.HEURISTIC algorithm: ModuleAlgorithm = algorithm.HEURISTIC
abstractAst = new AbstractAst() abstractAst: AbstractAst = new AbstractAst()
visit = this.abstractAst.build_visit( visit: Function = this.abstractAst.build_visit(
(node: any) => isStatement(node) || isSelfdestructCall(node.expression) (node: any) => isStatement(node) || isSelfdestructCall(node.expression)
) )
report = this.abstractAst.build_report(this._report.bind(this)) report: Function = this.abstractAst.build_report(this._report.bind(this))
private _report (contracts, multipleContractsWithSameName): ReportObj[] { private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] {
const warnings: ReportObj[] = [] const warnings: ReportObj[] = []
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
let hasSelf = false let hasSelf: boolean = false
func.relevantNodes.forEach((node) => { func.relevantNodes.forEach((node) => {
if (isSelfdestructCall(node)) { if (isSelfdestructCall(node)) {
warnings.push({ warnings.push({

@ -4,7 +4,7 @@ import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView' import AbstractAst from './abstractAstView'
import { get } from 'fast-levenshtein' import { get } from 'fast-levenshtein'
import { util } from 'remix-lib' import { util } from 'remix-lib'
import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, AstNodeLegacy, CompilationResult} from './../../types' import { AnalyzerModule, ModuleAlgorithm, ModuleCategory, ReportObj, ContractHLAst, ContractCallGraph, FunctionHLAst, VariableDeclarationAstNode} from './../../types'
export default class similarVariableNames implements AnalyzerModule { export default class similarVariableNames implements AnalyzerModule {
name: string = 'Similar variable names: ' name: string = 'Similar variable names: '
@ -14,32 +14,32 @@ export default class similarVariableNames implements AnalyzerModule {
abstractAst:AbstractAst = new AbstractAst() abstractAst:AbstractAst = new AbstractAst()
visit = this.abstractAst.build_visit((node: AstNodeLegacy) => false) visit: Function = this.abstractAst.build_visit((node: any) => false)
report = this.abstractAst.build_report(this._report.bind(this)) report: Function = this.abstractAst.build_report(this._report.bind(this))
private _report (contracts, multipleContractsWithSameName): ReportObj[] { private _report (contracts: ContractHLAst[], multipleContractsWithSameName: boolean): ReportObj[] {
const warnings: ReportObj[] = [] const warnings: ReportObj[] = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0) const hasModifiers: boolean = contracts.some((item) => item.modifiers.length > 0)
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) const funcName: string = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let hasModifiersComments = '' let hasModifiersComments: string = ''
if (hasModifiers) { if (hasModifiers) {
hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.' hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.'
} }
let multipleContractsWithSameNameComments = '' let multipleContractsWithSameNameComments: string = ''
if (multipleContractsWithSameName) { if (multipleContractsWithSameName) {
multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.' multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.'
} }
const vars = this.getFunctionVariables(contract, func).map(getDeclaredVariableName) const vars: string[] = this.getFunctionVariables(contract, func).map(getDeclaredVariableName)
this.findSimilarVarNames(vars).map((sim) => { this.findSimilarVarNames(vars).map((sim) => {
warnings.push({ warnings.push({
warning: `${funcName} : Variables have very similar names ${sim.var1} and ${sim.var2}. ${hasModifiersComments} ${multipleContractsWithSameNameComments}`, warning: `${funcName} : Variables have very similar names ${sim.var1} and ${sim.var2}. ${hasModifiersComments} ${multipleContractsWithSameNameComments}`,
location: func.src location: func['src']
}) })
}) })
}) })
@ -70,7 +70,7 @@ export default class similarVariableNames implements AnalyzerModule {
return varName2.match(ref) != null return varName2.match(ref) != null
} }
private getFunctionVariables (contract, func) { private getFunctionVariables (contract: ContractHLAst, func: FunctionHLAst): VariableDeclarationAstNode[] {
return contract.stateVariables.concat(func.localVariables) return contract.stateVariables.concat(func.localVariables)
} }
} }

@ -292,7 +292,7 @@ function getDeclaredVariableType (varDeclNode: VariableDeclarationAstNode): stri
* @contractNode {ASTNode} Contract Definition node * @contractNode {ASTNode} Contract Definition node
* @return {list variable declaration} state variable node list * @return {list variable declaration} state variable node list
*/ */
function getStateVariableDeclarationsFormContractNode (contractNode: ContractDefinitionAstNode): CommonAstNode[] { function getStateVariableDeclarationsFormContractNode (contractNode: ContractDefinitionAstNode): VariableDeclarationAstNode[] {
return contractNode.nodes.filter(el => el.nodeType === "VariableDeclaration") return contractNode.nodes.filter(el => el.nodeType === "VariableDeclaration")
} }

@ -41,6 +41,47 @@ export interface CompilationResult {
} }
} }
export interface ContractHLAst {
node: ContractDefinitionAstNode,
functions: FunctionHLAst[],
relevantNodes: any[],
modifiers: ModifierHLAst[],
inheritsFrom: any[],
stateVariables: VariableDeclarationAstNode[]
}
export interface FunctionHLAst {
node: FunctionDefinitionAstNode,
relevantNodes: any[],
modifierInvocations: ModifierInvocationAstNode[],
localVariables: VariableDeclarationAstNode[],
parameters: string[],
returns: Record<string, string>[]
}
export interface ModifierHLAst {
node: ModifierDefinitionAstNode,
relevantNodes: any[],
localVariables: VariableDeclarationAstNode[],
parameters: string[],
}
export interface Context {
callGraph: Record<string, ContractCallGraph>
currentContract: ContractHLAst
stateVariables: VariableDeclarationAstNode[]
}
export interface FunctionCallGraph {
node: FunctionHLAst
calls: any[]
}
export interface ContractCallGraph {
contract: ContractHLAst
functions: Record<string, FunctionCallGraph>
}
///////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////
///////////// Specfic AST Nodes ///////////////////////////// ///////////// Specfic AST Nodes /////////////////////////////
///////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////
@ -95,7 +136,7 @@ export interface ContractDefinitionAstNode {
linearizedBaseContracts: Array<number> linearizedBaseContracts: Array<number>
baseContracts: Array<InheritanceSpecifierAstNode> baseContracts: Array<InheritanceSpecifierAstNode>
contractDependencies: Array<number> contractDependencies: Array<number>
nodes: Array<CommonAstNode> nodes: Array<any>
scope: number scope: number
} }

@ -51,492 +51,492 @@ test('staticAnalysisCommon.helpers.buildFunctionSignature', function (t) {
// #################### Node Identification Primitives // #################### Node Identification Primitives
test('staticAnalysisCommon.helpers.name', function (t) { // test('staticAnalysisCommon.helpers.name', function (t) {
t.plan(9) // t.plan(9)
const node = { attributes: { value: 'now' } } // const node = { attributes: { value: 'now' } }
const node2 = { attributes: { member_name: 'call' } } // const node2 = { attributes: { member_name: 'call' } }
t.ok(common.helpers.memName(node, 'now'), 'should work for values')
t.ok(common.helpers.memName(node2, 'call'), 'should work for member_name')
t.ok(common.helpers.memName(node2, '.all'), 'regex should work')
lowlevelAccessersCommon(t, common.helpers.memName, node) // t.ok(common.helpers.memName(node, 'now'), 'should work for values')
}) // t.ok(common.helpers.memName(node2, 'call'), 'should work for member_name')
// t.ok(common.helpers.memName(node2, '.all'), 'regex should work')
test('staticAnalysisCommon.helpers.operator', function (t) { // lowlevelAccessersCommon(t, common.helpers.memName, node)
t.plan(10) // })
const node = { attributes: { operator: '++' } }
const node2 = { attributes: { operator: '+++' } }
const escapedPP = escapeRegExp('++') // test('staticAnalysisCommon.helpers.operator', function (t) {
const escapedPPExact = `^${escapedPP}$` // t.plan(10)
// const node = { attributes: { operator: '++' } }
// const node2 = { attributes: { operator: '+++' } }
t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++') // const escapedPP = escapeRegExp('++')
t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++') // const escapedPPExact = `^${escapedPP}$`
t.ok(common.helpers.operator(node, escapedPP), 'should work for ++')
t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++')
lowlevelAccessersCommon(t, common.helpers.operator, node) // t.ok(common.helpers.operator(node, escapedPPExact), 'should work for ++')
}) // t.notOk(common.helpers.operator(node2, escapedPPExact), 'should not work for +++')
// t.ok(common.helpers.operator(node, escapedPP), 'should work for ++')
// t.ok(common.helpers.operator(node2, escapedPP), 'should work for +++')
test('staticAnalysisCommon.helpers.nodeType', function (t) { // lowlevelAccessersCommon(t, common.helpers.operator, node)
t.plan(9) // })
const node = { name: 'Identifier', attributes: { name: 'now' } }
const node2 = { name: 'FunctionCall', attributes: { member_name: 'call' } }
t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for ident') // test('staticAnalysisCommon.helpers.nodeType', function (t) {
t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for funcall') // t.plan(9)
t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for funcall') // const node = { name: 'Identifier', attributes: { name: 'now' } }
// const node2 = { name: 'FunctionCall', attributes: { member_name: 'call' } }
lowlevelAccessersCommon(t, common.helpers.nodeType, node) // t.ok(common.helpers.nodeType(node, common.nodeTypes.IDENTIFIER), 'should work for ident')
}) // t.ok(common.helpers.nodeType(node2, common.nodeTypes.FUNCTIONCALL), 'should work for funcall')
// t.ok(common.helpers.nodeType(node2, '^F'), 'regex should work for funcall')
test('staticAnalysisCommon.helpers.expressionType', function (t) { // lowlevelAccessersCommon(t, common.helpers.nodeType, node)
t.plan(9) // })
const node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
const node2 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
t.ok(common.helpers.expressionType(node, common.basicTypes.UINT), 'should work for ident') // test('staticAnalysisCommon.helpers.expressionType', function (t) {
t.ok(common.helpers.expressionType(node2, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall') // t.plan(9)
t.ok(common.helpers.expressionType(node2, '^function \\('), 'regex should work') // const node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
// const node2 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
lowlevelAccessersCommon(t, common.helpers.expressionType, node) // t.ok(common.helpers.expressionType(node, common.basicTypes.UINT), 'should work for ident')
}) // t.ok(common.helpers.expressionType(node2, escapeRegExp(common.basicFunctionTypes.CALL)), 'should work for funcall')
// t.ok(common.helpers.expressionType(node2, '^function \\('), 'regex should work')
// test('staticAnalysisCommon.helpers.nrOfChildren', function (t) { // lowlevelAccessersCommon(t, common.helpers.expressionType, node)
// t.plan(10) // })
// const node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
// const node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
// const node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
// t.ok(common.helpers.nrOfChildren(node, 2), 'should work for 2 children')
// t.notOk(common.helpers.nrOfChildren(node, '1+2'), 'regex should not work')
// t.ok(common.helpers.nrOfChildren(node2, 0), 'should work for 0 children')
// t.ok(common.helpers.nrOfChildren(node3, 0), 'should work without children arr')
// lowlevelAccessersCommon(t, common.helpers.nrOfChildren, node)
// })
// test('staticAnalysisCommon.helpers.minNrOfChildren', function (t) {
// t.plan(13)
// const node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
// const node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
// const node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
// t.ok(common.helpers.minNrOfChildren(node, 2), 'should work for 2 children')
// t.ok(common.helpers.minNrOfChildren(node, 1), 'should work for 1 children')
// t.ok(common.helpers.minNrOfChildren(node, 0), 'should work for 0 children')
// t.notOk(common.helpers.minNrOfChildren(node, 3), 'has less than 3 children')
// t.notOk(common.helpers.minNrOfChildren(node, '1+2'), 'regex should not work')
// t.ok(common.helpers.minNrOfChildren(node2, 0), 'should work for 0 children')
// t.ok(common.helpers.minNrOfChildren(node3, 0), 'should work without children arr')
// lowlevelAccessersCommon(t, common.helpers.minNrOfChildren, node)
// })
function lowlevelAccessersCommon (t, f, someNode) {
t.ok(f(someNode), 'always ok if type is undefinded')
t.ok(f(someNode, undefined), 'always ok if name is undefinded 2')
t.notOk(f(null, undefined), 'false on no node')
t.notOk(f(null, 'call'), 'false on no node')
t.notOk(f(undefined, null), 'false on no node')
t.notOk(f(), 'false on no params')
}
// #################### Trivial Getter Test // // test('staticAnalysisCommon.helpers.nrOfChildren', function (t) {
// // t.plan(10)
test('staticAnalysisCommon.getType', function (t) { // // const node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
t.plan(2) // // const node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
const node = { "argumentTypes": null, // // const node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
"id": 3,
"name": "a", // // t.ok(common.helpers.nrOfChildren(node, 2), 'should work for 2 children')
"nodeType": "Identifier", // // t.notOk(common.helpers.nrOfChildren(node, '1+2'), 'regex should not work')
"overloadedDeclarations": [], // // t.ok(common.helpers.nrOfChildren(node2, 0), 'should work for 0 children')
"referencedDeclaration": 22, // // t.ok(common.helpers.nrOfChildren(node3, 0), 'should work without children arr')
"src": "52:1:0",
"typeDescriptions": // // lowlevelAccessersCommon(t, common.helpers.nrOfChildren, node)
{ // // })
"typeIdentifier": "t_uint256",
"typeString": "uint256" // // test('staticAnalysisCommon.helpers.minNrOfChildren', function (t) {
} // // t.plan(13)
} // // const node = { name: 'Identifier', children: ['a', 'b'], attributes: { value: 'now', type: 'uint256' } }
t.ok(common.getType(blockHashAccess) === 'function (uint256) view returns (bytes32)', 'gettype should work for different nodes') // // const node2 = { name: 'FunctionCall', children: [], attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes') // // const node3 = { name: 'FunctionCall', attributes: { member_name: 'call', type: 'function () payable returns (bool)' } }
})
// // t.ok(common.helpers.minNrOfChildren(node, 2), 'should work for 2 children')
// // t.ok(common.helpers.minNrOfChildren(node, 1), 'should work for 1 children')
// // t.ok(common.helpers.minNrOfChildren(node, 0), 'should work for 0 children')
// // t.notOk(common.helpers.minNrOfChildren(node, 3), 'has less than 3 children')
// // t.notOk(common.helpers.minNrOfChildren(node, '1+2'), 'regex should not work')
// // t.ok(common.helpers.minNrOfChildren(node2, 0), 'should work for 0 children')
// // t.ok(common.helpers.minNrOfChildren(node3, 0), 'should work without children arr')
// // lowlevelAccessersCommon(t, common.helpers.minNrOfChildren, node)
// // })
// function lowlevelAccessersCommon (t, f, someNode) {
// t.ok(f(someNode), 'always ok if type is undefinded')
// t.ok(f(someNode, undefined), 'always ok if name is undefinded 2')
// t.notOk(f(null, undefined), 'false on no node')
// t.notOk(f(null, 'call'), 'false on no node')
// t.notOk(f(undefined, null), 'false on no node')
// t.notOk(f(), 'false on no params')
// }
// // #################### Trivial Getter Test
// test('staticAnalysisCommon.getType', function (t) {
// t.plan(2)
// const node = { "argumentTypes": null,
// "id": 3,
// "name": "a",
// "nodeType": "Identifier",
// "overloadedDeclarations": [],
// "referencedDeclaration": 22,
// "src": "52:1:0",
// "typeDescriptions":
// {
// "typeIdentifier": "t_uint256",
// "typeString": "uint256"
// }
// }
// t.ok(common.getType(blockHashAccess) === 'function (uint256) view returns (bytes32)', 'gettype should work for different nodes')
// t.ok(common.getType(node) === 'uint256', 'gettype should work for different nodes')
// })
// #################### Complex Getter Test // // #################### Complex Getter Test
test('staticAnalysisCommon.getFunctionCallType', function (t) { // test('staticAnalysisCommon.getFunctionCallType', function (t) {
t.plan(5) // t.plan(5)
t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type') // t.equal(common.getFunctionCallType(libCall), 'function (struct Set.Data storage pointer,uint256) returns (bool)', 'this lib call returns correct type')
t.equal(common.getFunctionCallType(thisLocalCall), 'function (bytes32,address) returns (bool)', 'this local call returns correct type') // t.equal(common.getFunctionCallType(thisLocalCall), 'function (bytes32,address) returns (bool)', 'this local call returns correct type')
t.equal(common.getFunctionCallType(externalDirect), 'function () payable external returns (uint256)', 'external direct call returns correct type') // t.equal(common.getFunctionCallType(externalDirect), 'function () payable external returns (uint256)', 'external direct call returns correct type')
t.equal(common.getFunctionCallType(localCall), 'function (struct Ballot.Voter storage pointer)', 'local call returns correct type') // t.equal(common.getFunctionCallType(localCall), 'function (struct Ballot.Voter storage pointer)', 'local call returns correct type')
t.throws(() => common.getFunctionCallType({ name: 'MemberAccess' }), Error, 'throws on wrong type') // t.throws(() => common.getFunctionCallType({ name: 'MemberAccess' }), Error, 'throws on wrong type')
}) // })
test('staticAnalysisCommon.getEffectedVariableName', function (t) { // test('staticAnalysisCommon.getEffectedVariableName', function (t) {
t.plan(3) // t.plan(3)
t.throws(() => common.getEffectedVariableName(inlineAssembly), Error, 'staticAnalysisCommon.js: not an effect Node or inline assembly, get from inline assembly should throw') // t.throws(() => common.getEffectedVariableName(inlineAssembly), Error, 'staticAnalysisCommon.js: not an effect Node or inline assembly, get from inline assembly should throw')
t.ok(common.getEffectedVariableName(assignment) === 'c', 'get right name for assignment') // t.ok(common.getEffectedVariableName(assignment) === 'c', 'get right name for assignment')
t.throws(() => common.getEffectedVariableName({ name: 'MemberAccess' }), Error, 'should throw on all other nodes') // t.throws(() => common.getEffectedVariableName({ name: 'MemberAccess' }), Error, 'should throw on all other nodes')
}) // })
test('staticAnalysisCommon.getLocalCallName', function (t) { // test('staticAnalysisCommon.getLocalCallName', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.getLocalCallName(localCall) === 'bli', 'getLocal call name from node') // t.ok(common.getLocalCallName(localCall) === 'bli', 'getLocal call name from node')
t.throws(() => common.getLocalCallName(externalDirect), Error, 'throws on other nodes') // t.throws(() => common.getLocalCallName(externalDirect), Error, 'throws on other nodes')
t.throws(() => common.getLocalCallName(thisLocalCall), Error, 'throws on other nodes') // t.throws(() => common.getLocalCallName(thisLocalCall), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getThisLocalCallName', function (t) { // test('staticAnalysisCommon.getThisLocalCallName', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.getThisLocalCallName(thisLocalCall) === 'b', 'get this Local call name from node') // t.ok(common.getThisLocalCallName(thisLocalCall) === 'b', 'get this Local call name from node')
t.throws(() => common.getThisLocalCallName(externalDirect), Error, 'throws on other nodes') // t.throws(() => common.getThisLocalCallName(externalDirect), Error, 'throws on other nodes')
t.throws(() => common.getThisLocalCallName(localCall), Error, 'throws on other nodes') // t.throws(() => common.getThisLocalCallName(localCall), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getSuperLocalCallName', function (t) { // test('staticAnalysisCommon.getSuperLocalCallName', function (t) {
t.plan(4) // t.plan(4)
t.equal(common.getSuperLocalCallName(superLocal), 'duper', 'get local name from super local call') // t.equal(common.getSuperLocalCallName(superLocal), 'duper', 'get local name from super local call')
t.throws(() => common.getSuperLocalCallName(thisLocalCall), 'throws on other nodes') // t.throws(() => common.getSuperLocalCallName(thisLocalCall), 'throws on other nodes')
t.throws(() => common.getSuperLocalCallName(externalDirect), 'throws on other nodes') // t.throws(() => common.getSuperLocalCallName(externalDirect), 'throws on other nodes')
t.throws(() => common.getSuperLocalCallName(localCall), 'throws on other nodes') // t.throws(() => common.getSuperLocalCallName(localCall), 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) { // test('staticAnalysisCommon.getExternalDirectCallContractName', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.getExternalDirectCallContractName(externalDirect) === 'InfoFeed', 'external direct call contract name from node') // t.ok(common.getExternalDirectCallContractName(externalDirect) === 'InfoFeed', 'external direct call contract name from node')
t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), Error, 'throws on other nodes') // t.throws(() => common.getExternalDirectCallContractName(thisLocalCall), Error, 'throws on other nodes')
t.throws(() => common.getExternalDirectCallContractName(localCall), Error, 'throws on other nodes') // t.throws(() => common.getExternalDirectCallContractName(localCall), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getThisLocalCallContractName', function (t) { // test('staticAnalysisCommon.getThisLocalCallContractName', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'test', 'this local call contract name from node') // t.ok(common.getThisLocalCallContractName(thisLocalCall) === 'test', 'this local call contract name from node')
t.throws(() => common.getThisLocalCallContractName(localCall), Error, 'throws on other nodes') // t.throws(() => common.getThisLocalCallContractName(localCall), Error, 'throws on other nodes')
t.throws(() => common.getThisLocalCallContractName(externalDirect), Error, 'throws on other nodes') // t.throws(() => common.getThisLocalCallContractName(externalDirect), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) { // test('staticAnalysisCommon.getExternalDirectCallMemberName', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'info', 'external direct call name from node') // t.ok(common.getExternalDirectCallMemberName(externalDirect) === 'info', 'external direct call name from node')
t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), Error, 'throws on other nodes') // t.throws(() => common.getExternalDirectCallMemberName(thisLocalCall), Error, 'throws on other nodes')
t.throws(() => common.getExternalDirectCallMemberName(localCall), Error, 'throws on other nodes') // t.throws(() => common.getExternalDirectCallMemberName(localCall), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getContractName', function (t) { // test('staticAnalysisCommon.getContractName', function (t) {
t.plan(2) // t.plan(2)
const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } // const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
t.ok(common.getContractName(contract) === 'baz', 'returns right contract name') // t.ok(common.getContractName(contract) === 'baz', 'returns right contract name')
t.throws(() => common.getContractName({ name: 'InheritanceSpecifier' }), Error, 'throws on other nodes') // t.throws(() => common.getContractName({ name: 'InheritanceSpecifier' }), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getFunctionDefinitionName', function (t) { // test('staticAnalysisCommon.getFunctionDefinitionName', function (t) {
t.plan(2) // t.plan(2)
const func = { name: 'FunctionDefinition', attributes: { name: 'foo' } } // const func = { name: 'FunctionDefinition', attributes: { name: 'foo' } }
t.ok(common.getFunctionDefinitionName(func) === 'foo', 'returns right contract name') // t.ok(common.getFunctionDefinitionName(func) === 'foo', 'returns right contract name')
t.throws(() => common.getFunctionDefinitionName({ name: 'InlineAssembly' }), Error, 'throws on other nodes') // t.throws(() => common.getFunctionDefinitionName({ name: 'InlineAssembly' }), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getInheritsFromName', function (t) { // test('staticAnalysisCommon.getInheritsFromName', function (t) {
t.plan(2) // t.plan(2)
t.ok(common.getInheritsFromName(inheritance) === 'r', 'returns right contract name') // t.ok(common.getInheritsFromName(inheritance) === 'r', 'returns right contract name')
t.throws(() => common.getInheritsFromName({ name: 'ElementaryTypeName' }), Error, 'throws on other nodes') // t.throws(() => common.getInheritsFromName({ name: 'ElementaryTypeName' }), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getDeclaredVariableName', function (t) { // test('staticAnalysisCommon.getDeclaredVariableName', function (t) {
t.plan(2) // t.plan(2)
t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'x', 'extract right variable name') // t.ok(common.getDeclaredVariableName(storageVariableNodes.node1) === 'x', 'extract right variable name')
let node1 = JSON.parse(JSON.stringify(storageVariableNodes)) // let node1 = JSON.parse(JSON.stringify(storageVariableNodes))
node1.node1.name = 'FunctionCall' // node1.node1.name = 'FunctionCall'
t.throws(() => common.getDeclaredVariableName(node1) === 'x', Error, 'throw if wrong node') // t.throws(() => common.getDeclaredVariableName(node1) === 'x', Error, 'throw if wrong node')
}) // })
test('staticAnalysisCommon.getStateVariableDeclarationsFormContractNode', function (t) { // test('staticAnalysisCommon.getStateVariableDeclarationsFormContractNode', function (t) {
t.plan(4) // t.plan(4)
const res = common.getStateVariableDeclarationsFormContractNode(stateVariableContractNode).map(common.getDeclaredVariableName) // const res = common.getStateVariableDeclarationsFormContractNode(stateVariableContractNode).map(common.getDeclaredVariableName)
t.ok(res[0] === 'chairperson', 'var 1 should be ') // t.ok(res[0] === 'chairperson', 'var 1 should be ')
t.ok(res[1] === 'voters', 'var 2 should be ') // t.ok(res[1] === 'voters', 'var 2 should be ')
t.ok(res[2] === 'proposals', 'var 3 should be ') // t.ok(res[2] === 'proposals', 'var 3 should be ')
t.ok(res[3] === undefined, 'var 4 should be undefined') // t.ok(res[3] === undefined, 'var 4 should be undefined')
}) // })
test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) { // test('staticAnalysisCommon.getFunctionOrModifierDefinitionParameterPart', function (t) {
t.plan(2) // t.plan(2)
t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList') // t.ok(common.helpers.nodeType(common.getFunctionOrModifierDefinitionParameterPart(functionDefinition), 'ParameterList'), 'should return a parameterList')
t.throws(() => common.getFunctionOrModifierDefinitionParameterPart({ name: 'SourceUnit' }), Error, 'throws on other nodes') // t.throws(() => common.getFunctionOrModifierDefinitionParameterPart({ name: 'SourceUnit' }), Error, 'throws on other nodes')
}) // })
test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) { // test('staticAnalysisCommon.getFunctionCallTypeParameterType', function (t) {
t.plan(4) // t.plan(4)
t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === 'bytes32,address', 'this local call returns correct type') // t.ok(common.getFunctionCallTypeParameterType(thisLocalCall) === 'bytes32,address', 'this local call returns correct type')
t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type') // t.ok(common.getFunctionCallTypeParameterType(externalDirect) === '', 'external direct call returns correct type')
t.ok(common.getFunctionCallTypeParameterType(localCall) === 'struct Ballot.Voter storage pointer', 'local call returns correct type') // t.ok(common.getFunctionCallTypeParameterType(localCall) === 'struct Ballot.Voter storage pointer', 'local call returns correct type')
t.throws(() => common.getFunctionCallTypeParameterType({ name: 'MemberAccess' }), Error, 'throws on wrong type') // t.throws(() => common.getFunctionCallTypeParameterType({ name: 'MemberAccess' }), Error, 'throws on wrong type')
}) // })
test('staticAnalysisCommon.getLibraryCallContractName', function (t) { // test('staticAnalysisCommon.getLibraryCallContractName', function (t) {
t.plan(2) // t.plan(2)
t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name') // t.equal(common.getLibraryCallContractName(libCall), 'Set', 'should return correct contract name')
t.throws(() => common.getLibraryCallContractName({ name: 'Identifier' }), Error, 'should throw on wrong node') // t.throws(() => common.getLibraryCallContractName({ name: 'Identifier' }), Error, 'should throw on wrong node')
}) // })
test('staticAnalysisCommon.getLibraryCallMemberName', function (t) { // test('staticAnalysisCommon.getLibraryCallMemberName', function (t) {
t.plan(2) // t.plan(2)
t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name') // t.equal(common.getLibraryCallMemberName(libCall), 'insert', 'should return correct member name')
t.throws(() => common.getLibraryCallMemberName({ name: 'Identifier' }), Error, 'should throw on wrong node') // t.throws(() => common.getLibraryCallMemberName({ name: 'Identifier' }), Error, 'should throw on wrong node')
}) // })
test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) { // test('staticAnalysisCommon.getFullQualifiedFunctionCallIdent', function (t) {
t.plan(4) // t.plan(4)
const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } // const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
t.ok(common.getFullQualifiedFunctionCallIdent(contract, thisLocalCall) === 'test.b(bytes32,address)', 'this local call returns correct type') // t.ok(common.getFullQualifiedFunctionCallIdent(contract, thisLocalCall) === 'test.b(bytes32,address)', 'this local call returns correct type')
t.ok(common.getFullQualifiedFunctionCallIdent(contract, externalDirect) === 'InfoFeed.info()', 'external direct call returns correct type') // t.ok(common.getFullQualifiedFunctionCallIdent(contract, externalDirect) === 'InfoFeed.info()', 'external direct call returns correct type')
t.ok(common.getFullQualifiedFunctionCallIdent(contract, localCall) === 'baz.bli(struct Ballot.Voter storage pointer)', 'local call returns correct type') // t.ok(common.getFullQualifiedFunctionCallIdent(contract, localCall) === 'baz.bli(struct Ballot.Voter storage pointer)', 'local call returns correct type')
t.throws(() => common.getFullQualifiedFunctionCallIdent(contract, { name: 'MemberAccess' }), Error, 'throws on wrong type') // t.throws(() => common.getFullQualifiedFunctionCallIdent(contract, { name: 'MemberAccess' }), Error, 'throws on wrong type')
}) // })
test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) { // test('staticAnalysisCommon.getFullQuallyfiedFuncDefinitionIdent', function (t) {
t.plan(3) // t.plan(3)
const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } } // const contract = { name: 'ContractDefinition', attributes: { name: 'baz' } }
t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contract, fullyQualifiedFunctionDefinition, ['uint256', 'bool']) === 'baz.getY(uint256,bool)', 'creates right signature') // t.ok(common.getFullQuallyfiedFuncDefinitionIdent(contract, fullyQualifiedFunctionDefinition, ['uint256', 'bool']) === 'baz.getY(uint256,bool)', 'creates right signature')
t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contract, { name: 'MemberAccess' }, ['uint256', 'bool']), Error, 'throws on wrong nodes') // t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent(contract, { name: 'MemberAccess' }, ['uint256', 'bool']), Error, 'throws on wrong nodes')
t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent({ name: 'FunctionCall' }, fullyQualifiedFunctionDefinition, ['uint256', 'bool']), Error, 'throws on wrong nodes') // t.throws(() => common.getFullQuallyfiedFuncDefinitionIdent({ name: 'FunctionCall' }, fullyQualifiedFunctionDefinition, ['uint256', 'bool']), Error, 'throws on wrong nodes')
}) // })
// #################### Complex Node Identification // // #################### Complex Node Identification
test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) { // test('staticAnalysisCommon.isBuiltinFunctionCall', function (t) {
t.plan(2) // t.plan(2)
t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin') // t.ok(common.isBuiltinFunctionCall(selfdestruct), 'selfdestruct is builtin')
t.notOk(common.isBuiltinFunctionCall(localCall), 'local call is not builtin') // t.notOk(common.isBuiltinFunctionCall(localCall), 'local call is not builtin')
}) // })
test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) { // test('staticAnalysisCommon.isStorageVariableDeclaration', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage') // t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node1), 'struct storage pointer param is storage')
t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage') // t.ok(common.isStorageVariableDeclaration(storageVariableNodes.node2), 'struct storage pointer mapping param is storage')
t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage') // t.notOk(common.isStorageVariableDeclaration(storageVariableNodes.node3), 'bytes is not storage')
}) // })
test('staticAnalysisCommon.isInteraction', function (t) { // test('staticAnalysisCommon.isInteraction', function (t) {
t.plan(6) // t.plan(6)
t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction') // t.ok(common.isInteraction(lowlevelCall.sendAst), 'send is interaction')
t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction') // t.ok(common.isInteraction(lowlevelCall.callAst), 'call is interaction')
t.ok(common.isInteraction(externalDirect), 'ExternalDirecCall is interaction') // t.ok(common.isInteraction(externalDirect), 'ExternalDirecCall is interaction')
t.notOk(common.isInteraction(lowlevelCall.callcodeAst), 'callcode is not interaction') // t.notOk(common.isInteraction(lowlevelCall.callcodeAst), 'callcode is not interaction')
t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'callcode is not interaction') // t.notOk(common.isInteraction(lowlevelCall.delegatecallAst), 'callcode is not interaction')
t.notOk(common.isInteraction(localCall), 'local call is not interaction') // t.notOk(common.isInteraction(localCall), 'local call is not interaction')
}) // })
test('staticAnalysisCommon.isEffect', function (t) { // test('staticAnalysisCommon.isEffect', function (t) {
t.plan(5) // t.plan(5)
const unaryOp = { name: 'UnaryOperation', attributes: { operator: '++' } } // const unaryOp = { name: 'UnaryOperation', attributes: { operator: '++' } }
t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect') // t.ok(common.isEffect(inlineAssembly), 'inline assembly is treated as effect')
t.ok(common.isEffect(assignment), 'assignment is treated as effect') // t.ok(common.isEffect(assignment), 'assignment is treated as effect')
t.ok(common.isEffect(unaryOp), '++ is treated as effect') // t.ok(common.isEffect(unaryOp), '++ is treated as effect')
unaryOp.attributes.operator = '--' // unaryOp.attributes.operator = '--'
t.ok(common.isEffect(unaryOp), '-- is treated as effect') // t.ok(common.isEffect(unaryOp), '-- is treated as effect')
t.notOk(common.isEffect({ name: 'MemberAccess', attributes: { operator: '++' } }), 'MemberAccess not treated as effect') // t.notOk(common.isEffect({ name: 'MemberAccess', attributes: { operator: '++' } }), 'MemberAccess not treated as effect')
}) // })
test('staticAnalysisCommon.isWriteOnStateVariable', function (t) { // test('staticAnalysisCommon.isWriteOnStateVariable', function (t) {
t.plan(3) // t.plan(3)
const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1)) // const node1 = JSON.parse(JSON.stringify(storageVariableNodes.node1))
const node2 = node1 // const node2 = node1
const node3 = node1 // const node3 = node1
node2.attributes.name = 'y' // node2.attributes.name = 'y'
node3.attributes.name = 'xx' // node3.attributes.name = 'xx'
t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state') // t.ok(common.isWriteOnStateVariable(inlineAssembly, [node1, node2, node3]), 'inline Assembly is write on state')
t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state') // t.notOk(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on non state is not write on state')
node3.attributes.name = 'c' // node3.attributes.name = 'c'
t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is not write on state') // t.ok(common.isWriteOnStateVariable(assignment, [node1, node2, node3]), 'assignment on state is not write on state')
}) // })
test('staticAnalysisCommon.isStateVariable', function (t) { // test('staticAnalysisCommon.isStateVariable', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.isStateVariable('x', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained') // t.ok(common.isStateVariable('x', [storageVariableNodes.node1, storageVariableNodes.node2]), 'is contained')
t.ok(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice') // t.ok(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node1, storageVariableNodes.node1]), 'is contained twice')
t.notOk(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained') // t.notOk(common.isStateVariable('x', [storageVariableNodes.node2, storageVariableNodes.node3]), 'not contained')
}) // })
test('staticAnalysisCommon.isConstantFunction', function (t) { // test('staticAnalysisCommon.isConstantFunction', function (t) {
t.plan(3) // t.plan(3)
const node1 = { name: 'FunctionDefinition', attributes: { constant: true, stateMutability: 'view' } } // const node1 = { name: 'FunctionDefinition', attributes: { constant: true, stateMutability: 'view' } }
const node2 = { name: 'FunctionDefinition', attributes: { constant: false, stateMutability: 'nonpayable' } } // const node2 = { name: 'FunctionDefinition', attributes: { constant: false, stateMutability: 'nonpayable' } }
const node3 = { name: 'MemberAccess', attributes: { constant: true, stateMutability: 'view' } } // const node3 = { name: 'MemberAccess', attributes: { constant: true, stateMutability: 'view' } }
t.ok(common.isConstantFunction(node1), 'should be const func definition') // t.ok(common.isConstantFunction(node1), 'should be const func definition')
t.notOk(common.isConstantFunction(node2), 'should not be const func definition') // t.notOk(common.isConstantFunction(node2), 'should not be const func definition')
t.notOk(common.isConstantFunction(node3), 'wrong node should not be const func definition') // t.notOk(common.isConstantFunction(node3), 'wrong node should not be const func definition')
}) // })
test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) { // test('staticAnalysisCommon.isPlusPlusUnaryOperation', function (t) {
t.plan(3) // t.plan(3)
const node1 = { name: 'UnaryOperation', attributes: { operator: '++' } } // const node1 = { name: 'UnaryOperation', attributes: { operator: '++' } }
const node2 = { name: 'UnaryOperation', attributes: { operator: '--' } } // const node2 = { name: 'UnaryOperation', attributes: { operator: '--' } }
const node3 = { name: 'FunctionDefinition', attributes: { operator: '++' } } // const node3 = { name: 'FunctionDefinition', attributes: { operator: '++' } }
t.ok(common.isPlusPlusUnaryOperation(node1), 'should be unary ++') // t.ok(common.isPlusPlusUnaryOperation(node1), 'should be unary ++')
t.notOk(common.isPlusPlusUnaryOperation(node2), 'should not be unary ++') // t.notOk(common.isPlusPlusUnaryOperation(node2), 'should not be unary ++')
t.notOk(common.isPlusPlusUnaryOperation(node3), 'wrong node should not be unary ++') // t.notOk(common.isPlusPlusUnaryOperation(node3), 'wrong node should not be unary ++')
}) // })
test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) { // test('staticAnalysisCommon.isMinusMinusUnaryOperation', function (t) {
t.plan(3) // t.plan(3)
const node1 = { name: 'UnaryOperation', attributes: { operator: '--' } } // const node1 = { name: 'UnaryOperation', attributes: { operator: '--' } }
const node2 = { name: 'UnaryOperation', attributes: { operator: '++' } } // const node2 = { name: 'UnaryOperation', attributes: { operator: '++' } }
const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } } // const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
t.ok(common.isMinusMinusUnaryOperation(node1), 'should be unary --') // t.ok(common.isMinusMinusUnaryOperation(node1), 'should be unary --')
t.notOk(common.isMinusMinusUnaryOperation(node2), 'should not be unary --') // t.notOk(common.isMinusMinusUnaryOperation(node2), 'should not be unary --')
t.notOk(common.isMinusMinusUnaryOperation(node3), 'wrong node should not be unary --') // t.notOk(common.isMinusMinusUnaryOperation(node3), 'wrong node should not be unary --')
}) // })
test('staticAnalysisCommon.isFullyImplementedContract', function (t) { // test('staticAnalysisCommon.isFullyImplementedContract', function (t) {
t.plan(3) // t.plan(3)
const node1 = { name: 'ContractDefinition', attributes: { fullyImplemented: true } } // const node1 = { name: 'ContractDefinition', attributes: { fullyImplemented: true } }
const node2 = { name: 'ContractDefinition', attributes: { fullyImplemented: false } } // const node2 = { name: 'ContractDefinition', attributes: { fullyImplemented: false } }
const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } } // const node3 = { name: 'FunctionDefinition', attributes: { operator: '--' } }
t.ok(common.isFullyImplementedContract(node1), 'should be fully implemented contract') // t.ok(common.isFullyImplementedContract(node1), 'should be fully implemented contract')
t.notOk(common.isFullyImplementedContract(node2), 'should not be fully implemented contract') // t.notOk(common.isFullyImplementedContract(node2), 'should not be fully implemented contract')
t.notOk(common.isFullyImplementedContract(node3), 'wrong node should not be fully implemented contract') // t.notOk(common.isFullyImplementedContract(node3), 'wrong node should not be fully implemented contract')
}) // })
test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) { // test('staticAnalysisCommon.isCallToNonConstLocalFunction', function (t) {
t.plan(2) // t.plan(2)
t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func') // t.ok(common.isCallToNonConstLocalFunction(localCall), 'should be call to non const Local func')
localCall.children[0].attributes.type = 'function (struct Ballot.Voter storage pointer) view payable (uint256)' // localCall.children[0].attributes.type = 'function (struct Ballot.Voter storage pointer) view payable (uint256)'
t.notok(common.isCallToNonConstLocalFunction(localCall), 'should no longer be call to non const Local func') // t.notok(common.isCallToNonConstLocalFunction(localCall), 'should no longer be call to non const Local func')
}) // })
test('staticAnalysisCommon.isExternalDirectCall', function (t) { // test('staticAnalysisCommon.isExternalDirectCall', function (t) {
t.plan(5) // t.plan(5)
const node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } } // const node2 = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(externalDirect), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(externalDirect), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(externalDirect), 'is now used should not work') // t.notOk(common.isNowAccess(externalDirect), 'is now used should not work')
t.ok(common.isExternalDirectCall(externalDirect), 'f.info() should be external direct call') // t.ok(common.isExternalDirectCall(externalDirect), 'f.info() should be external direct call')
t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call') // t.notOk(common.isExternalDirectCall(node2), 'local call is not an exernal call')
}) // })
test('staticAnalysisCommon.isNowAccess', function (t) { // test('staticAnalysisCommon.isNowAccess', function (t) {
t.plan(3) // t.plan(3)
const node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } } // const node = { name: 'Identifier', attributes: { value: 'now', type: 'uint256' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.ok(common.isNowAccess(node), 'is now used should work') // t.ok(common.isNowAccess(node), 'is now used should work')
}) // })
test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { // test('staticAnalysisCommon.isBlockTimestampAccess', function (t) {
t.plan(3) // t.plan(3)
const node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'timestamp', type: 'uint256' } } // const node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'timestamp', type: 'uint256' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.ok(common.isBlockTimestampAccess(node), 'is block.timestamp used should work') // t.ok(common.isBlockTimestampAccess(node), 'is block.timestamp used should work')
t.notOk(common.isNowAccess(node), 'is now used should not work') // t.notOk(common.isNowAccess(node), 'is now used should not work')
}) // })
test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) { // test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) {
t.plan(4) // t.plan(4)
t.notOk(common.isThisLocalCall(blockHashAccess), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(blockHashAccess), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(blockHashAccess), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(blockHashAccess), 'is block.timestamp used should not work')
t.ok(common.isBlockBlockHashAccess(blockHashAccess), 'blockhash should work') // todo: // t.ok(common.isBlockBlockHashAccess(blockHashAccess), 'blockhash should work') // todo:
t.notOk(common.isNowAccess(blockHashAccess), 'is now used should not work') // t.notOk(common.isNowAccess(blockHashAccess), 'is now used should not work')
}) // })
test('staticAnalysisCommon.isThisLocalCall', function (t) { // test('staticAnalysisCommon.isThisLocalCall', function (t) {
t.plan(3) // t.plan(3)
t.ok(common.isThisLocalCall(thisLocalCall), 'is this.local_method() used should work') // t.ok(common.isThisLocalCall(thisLocalCall), 'is this.local_method() used should work')
t.notOk(common.isBlockTimestampAccess(thisLocalCall), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(thisLocalCall), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(thisLocalCall), 'is now used should not work') // t.notOk(common.isNowAccess(thisLocalCall), 'is now used should not work')
}) // })
test('staticAnalysisCommon.isSuperLocalCall', function (t) { // test('staticAnalysisCommon.isSuperLocalCall', function (t) {
t.plan(4) // t.plan(4)
t.ok(common.isSuperLocalCall(superLocal), 'is super.local_method() used should work') // t.ok(common.isSuperLocalCall(superLocal), 'is super.local_method() used should work')
t.notOk(common.isThisLocalCall(superLocal), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(superLocal), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(superLocal), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(superLocal), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(superLocal), 'is now used should not work') // t.notOk(common.isNowAccess(superLocal), 'is now used should not work')
}) // })
test('staticAnalysisCommon.isLibraryCall', function (t) { // test('staticAnalysisCommon.isLibraryCall', function (t) {
t.plan(5) // t.plan(5)
t.ok(common.isLibraryCall(libCall), 'is lib call should not work') // t.ok(common.isLibraryCall(libCall), 'is lib call should not work')
t.notOk(common.isSuperLocalCall(libCall), 'is super.local_method() used should not work') // t.notOk(common.isSuperLocalCall(libCall), 'is super.local_method() used should not work')
t.notOk(common.isThisLocalCall(libCall), 'is this.local_method() used should not work') // t.notOk(common.isThisLocalCall(libCall), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(libCall), 'is block.timestamp used should not work') // t.notOk(common.isBlockTimestampAccess(libCall), 'is block.timestamp used should not work')
t.notOk(common.isNowAccess(libCall), 'is now used should not work') // t.notOk(common.isNowAccess(libCall), 'is now used should not work')
}) // })
test('staticAnalysisCommon.isLocalCall', function (t) { // test('staticAnalysisCommon.isLocalCall', function (t) {
t.plan(5) // t.plan(5)
t.ok(common.isLocalCall(localCall), 'isLocalCall') // t.ok(common.isLocalCall(localCall), 'isLocalCall')
t.notOk(common.isLowLevelCall(localCall), 'is not low level call') // t.notOk(common.isLowLevelCall(localCall), 'is not low level call')
t.notOk(common.isExternalDirectCall(localCall), 'is not external direct call') // t.notOk(common.isExternalDirectCall(localCall), 'is not external direct call')
t.notOk(common.isEffect(localCall), 'is not effect') // t.notOk(common.isEffect(localCall), 'is not effect')
t.notOk(common.isInteraction(localCall), 'is not interaction') // t.notOk(common.isInteraction(localCall), 'is not interaction')
}) // })
test('staticAnalysisCommon.isLowLevelCall', function (t) { // test('staticAnalysisCommon.isLowLevelCall', function (t) {
t.plan(6) // t.plan(6)
t.ok(common.isLowLevelSendInst(lowlevelCall.sendAst) && common.isLowLevelCall(lowlevelCall.sendAst), 'send is llc should work') // t.ok(common.isLowLevelSendInst(lowlevelCall.sendAst) && common.isLowLevelCall(lowlevelCall.sendAst), 'send is llc should work')
t.ok(common.isLowLevelCallInst(lowlevelCall.callAst) && common.isLowLevelCall(lowlevelCall.callAst), 'call is llc should work') // t.ok(common.isLowLevelCallInst(lowlevelCall.callAst) && common.isLowLevelCall(lowlevelCall.callAst), 'call is llc should work')
t.notOk(common.isLowLevelCallInst(lowlevelCall.callcodeAst), 'callcode is not call') // t.notOk(common.isLowLevelCallInst(lowlevelCall.callcodeAst), 'callcode is not call')
t.ok(common.isLowLevelCallcodeInst(lowlevelCall.callcodeAst) && common.isLowLevelCall(lowlevelCall.callcodeAst), 'callcode is llc should work') // t.ok(common.isLowLevelCallcodeInst(lowlevelCall.callcodeAst) && common.isLowLevelCall(lowlevelCall.callcodeAst), 'callcode is llc should work')
t.notOk(common.isLowLevelCallcodeInst(lowlevelCall.callAst), 'call is not callcode') // t.notOk(common.isLowLevelCallcodeInst(lowlevelCall.callAst), 'call is not callcode')
t.ok(common.isLowLevelDelegatecallInst(lowlevelCall.delegatecallAst) && common.isLowLevelCall(lowlevelCall.delegatecallAst), 'delegatecall is llc should work') // t.ok(common.isLowLevelDelegatecallInst(lowlevelCall.delegatecallAst) && common.isLowLevelCall(lowlevelCall.delegatecallAst), 'delegatecall is llc should work')
}) // })
test('staticAnalysisCommon: Call of parameter function', function (t) { // test('staticAnalysisCommon: Call of parameter function', function (t) {
t.plan(7) // t.plan(7)
t.ok(common.isLocalCall(parameterFunction), 'is not LocalCall') // t.ok(common.isLocalCall(parameterFunction), 'is not LocalCall')
t.notOk(common.isThisLocalCall(parameterFunction), 'is not this local call') // t.notOk(common.isThisLocalCall(parameterFunction), 'is not this local call')
t.notOk(common.isSuperLocalCall(parameterFunction), 'is not super local call') // t.notOk(common.isSuperLocalCall(parameterFunction), 'is not super local call')
t.notOk(common.isExternalDirectCall(parameterFunction), 'is not ExternalDirectCall') // t.notOk(common.isExternalDirectCall(parameterFunction), 'is not ExternalDirectCall')
t.notOk(common.isLibraryCall(parameterFunction), 'is not LibraryCall') // t.notOk(common.isLibraryCall(parameterFunction), 'is not LibraryCall')
t.equals(common.getFunctionCallType(parameterFunction), 'function (uint256,uint256) pure returns (uint256)', 'Extracts right type') // t.equals(common.getFunctionCallType(parameterFunction), 'function (uint256,uint256) pure returns (uint256)', 'Extracts right type')
t.equals(common.getFunctionCallTypeParameterType(parameterFunction), 'uint256,uint256', 'Extracts param right type') // t.equals(common.getFunctionCallTypeParameterType(parameterFunction), 'uint256,uint256', 'Extracts param right type')
}) // })
test('staticAnalysisCommon: function call with of function with function parameter', function (t) { // test('staticAnalysisCommon: function call with of function with function parameter', function (t) {
t.plan(2) // t.plan(2)
t.equals(common.getFunctionCallType(parameterFunctionCall), 'function (function (uint256,uint256) pure returns (uint256),uint256,uint256) pure returns (uint256)', 'Extracts right type') // t.equals(common.getFunctionCallType(parameterFunctionCall), 'function (function (uint256,uint256) pure returns (uint256),uint256,uint256) pure returns (uint256)', 'Extracts right type')
t.equals(common.getFunctionCallTypeParameterType(parameterFunctionCall), 'function (uint256,uint256) pure returns (uint256),uint256,uint256', 'Extracts param right type') // t.equals(common.getFunctionCallTypeParameterType(parameterFunctionCall), 'function (uint256,uint256) pure returns (uint256),uint256,uint256', 'Extracts param right type')
}) // })
test('staticAnalysisCommon: require call', function (t) { // test('staticAnalysisCommon: require call', function (t) {
t.plan(3) // t.plan(3)
const node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'tuple()', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bool', 'typeString': 'bool'}, {'typeIdentifier': 't_stringliteral_80efd193f332877914d93edb0b3ef5c6a7eecd00c6251c3fd7f146b60b40e6cd', 'typeString': 'literal_string \'fuu\''}], 'overloadedDeclarations': [90, 91], 'referencedDeclaration': 91, 'type': 'function (bool,string memory) pure', 'value': 'require'}, 'id': 50, 'name': 'Identifier', 'src': '462:7:0'}, {'attributes': {'argumentTypes': null, 'commonType': {'typeIdentifier': 't_address', 'typeString': 'address'}, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': '==', 'type': 'bool'}, 'children': [{'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'sender', 'referencedDeclaration': null, 'type': 'address'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 87, 'type': 'msg', 'value': 'msg'}, 'id': 51, 'name': 'Identifier', 'src': '470:3:0'}], 'id': 52, 'name': 'MemberAccess', 'src': '470:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 10, 'type': 'address', 'value': 'owner'}, 'id': 53, 'name': 'Identifier', 'src': '484:5:0'}], 'id': 54, 'name': 'BinaryOperation', 'src': '470:19:0'}, {'attributes': {'argumentTypes': null, 'hexvalue': '667575', 'isConstant': false, 'isLValue': false, 'isPure': true, 'lValueRequested': false, 'subdenomination': null, 'token': 'string', 'type': 'literal_string \'fuu\'', 'value': 'fuu'}, 'id': 55, 'name': 'Literal', 'src': '491:5:0'}], 'id': 56, 'name': 'FunctionCall', 'src': '462:35:0'} // const node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'tuple()', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bool', 'typeString': 'bool'}, {'typeIdentifier': 't_stringliteral_80efd193f332877914d93edb0b3ef5c6a7eecd00c6251c3fd7f146b60b40e6cd', 'typeString': 'literal_string \'fuu\''}], 'overloadedDeclarations': [90, 91], 'referencedDeclaration': 91, 'type': 'function (bool,string memory) pure', 'value': 'require'}, 'id': 50, 'name': 'Identifier', 'src': '462:7:0'}, {'attributes': {'argumentTypes': null, 'commonType': {'typeIdentifier': 't_address', 'typeString': 'address'}, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': '==', 'type': 'bool'}, 'children': [{'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'sender', 'referencedDeclaration': null, 'type': 'address'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 87, 'type': 'msg', 'value': 'msg'}, 'id': 51, 'name': 'Identifier', 'src': '470:3:0'}], 'id': 52, 'name': 'MemberAccess', 'src': '470:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 10, 'type': 'address', 'value': 'owner'}, 'id': 53, 'name': 'Identifier', 'src': '484:5:0'}], 'id': 54, 'name': 'BinaryOperation', 'src': '470:19:0'}, {'attributes': {'argumentTypes': null, 'hexvalue': '667575', 'isConstant': false, 'isLValue': false, 'isPure': true, 'lValueRequested': false, 'subdenomination': null, 'token': 'string', 'type': 'literal_string \'fuu\'', 'value': 'fuu'}, 'id': 55, 'name': 'Literal', 'src': '491:5:0'}], 'id': 56, 'name': 'FunctionCall', 'src': '462:35:0'}
t.equals(common.isRequireCall(node), true) // t.equals(common.isRequireCall(node), true)
t.equals(common.getFunctionCallType(node), 'function (bool,string memory) pure', 'Extracts right type') // t.equals(common.getFunctionCallType(node), 'function (bool,string memory) pure', 'Extracts right type')
t.equals(common.getFunctionCallTypeParameterType(node), 'bool,string memory', 'Extracts param right type') // t.equals(common.getFunctionCallTypeParameterType(node), 'bool,string memory', 'Extracts param right type')
}) // })
test('staticAnalysisCommon: isDeleteOfDynamicArray', function (t) { // test('staticAnalysisCommon: isDeleteOfDynamicArray', function (t) {
t.plan(2) // t.plan(2)
const node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': 'delete', 'prefix': true, 'type': 'tuple()'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 4, 'type': 'uint256[] storage ref', 'value': 'users'}, 'id': 58, 'name': 'Identifier', 'src': '514:5:0'}], 'id': 59, 'name': 'UnaryOperation', 'src': '507:12:0'} // const node = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'operator': 'delete', 'prefix': true, 'type': 'tuple()'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 4, 'type': 'uint256[] storage ref', 'value': 'users'}, 'id': 58, 'name': 'Identifier', 'src': '514:5:0'}], 'id': 59, 'name': 'UnaryOperation', 'src': '507:12:0'}
t.equals(common.isDeleteOfDynamicArray(node), true) // t.equals(common.isDeleteOfDynamicArray(node), true)
t.equals(common.isDynamicArrayAccess(node.children[0]), true, 'Extracts right type') // t.equals(common.isDynamicArrayAccess(node.children[0]), true, 'Extracts right type')
}) // })
test('staticAnalysisCommon: isAbiNamespaceCall', function (t) { // test('staticAnalysisCommon: isAbiNamespaceCall', function (t) {
t.plan(8) // t.plan(8)
const node1 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encode', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 26, 'name': 'Identifier', 'src': '245: 3:0'}], 'id': 28, 'name': 'MemberAccess', 'src': '245:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 29, 'name': 'Identifier', 'src': '256:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 30, 'name': 'Identifier', 'src': '258:1:0'}], 'id': 31, 'name': 'FunctionCall', 'src': '245:15:0'} // const node1 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encode', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 26, 'name': 'Identifier', 'src': '245: 3:0'}], 'id': 28, 'name': 'MemberAccess', 'src': '245:10:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 29, 'name': 'Identifier', 'src': '256:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 30, 'name': 'Identifier', 'src': '258:1:0'}], 'id': 31, 'name': 'FunctionCall', 'src': '245:15:0'}
const node2 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodePacked', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 33, 'name': 'Identifier', 'src': '279:3:0'}], 'id': 35, 'name': 'MemberAccess', 'src': '279:16:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 36, 'name': 'Identifier', 'src': '296:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 37, 'name': 'Identifier', 'src': '298:1:0'}], 'id': 38, 'name': 'FunctionCall', 'src': '279:21:0'} // const node2 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodePacked', 'referencedDeclaration': null, 'type': 'function () pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 33, 'name': 'Identifier', 'src': '279:3:0'}], 'id': 35, 'name': 'MemberAccess', 'src': '279:16:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 36, 'name': 'Identifier', 'src': '296:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 37, 'name': 'Identifier', 'src': '298:1:0'}], 'id': 38, 'name': 'FunctionCall', 'src': '279:21:0'}
const node3 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bytes4', 'typeString': 'bytes4'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSelector', 'referencedDeclaration': null, 'type': 'function (bytes4) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 40, 'name': 'Identifier', 'src': '319:3:0'}], 'id': 42, 'name': 'MemberAccess', 'src': '319:22:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 19, 'type': 'bytes4', 'value': 'selector'}, 'id': 43, 'name': 'Identifier', 'src': '342:8:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 44, 'name': 'Identifier', 'src': '352:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 45, 'name': 'Identifier', 'src': '355:1:0'}], 'id': 46, 'name': 'FunctionCall', 'src': '319:38:0'} // const node3 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_bytes4', 'typeString': 'bytes4'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSelector', 'referencedDeclaration': null, 'type': 'function (bytes4) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 40, 'name': 'Identifier', 'src': '319:3:0'}], 'id': 42, 'name': 'MemberAccess', 'src': '319:22:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 19, 'type': 'bytes4', 'value': 'selector'}, 'id': 43, 'name': 'Identifier', 'src': '342:8:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 44, 'name': 'Identifier', 'src': '352:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 45, 'name': 'Identifier', 'src': '355:1:0'}], 'id': 46, 'name': 'FunctionCall', 'src': '319:38:0'}
const node4 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_string_memory_ptr', 'typeString': 'string memory'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSignature', 'referencedDeclaration': null, 'type': 'function (string memory) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 48, 'name': 'Identifier', 'src': '367:3:0'}], 'id': 50, 'name': 'MemberAccess', 'src': '367:23:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 11, 'type': 'string memory', 'value': 'sig'}, 'id': 51, 'name': 'Identifier', 'src': '391:3:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 52, 'name': 'Identifier', 'src': '396:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 53, 'name': 'Identifier', 'src': '399:1:0'}], 'id': 54, 'name': 'FunctionCall', 'src': '367:34:0'} // const node4 = {'attributes': {'argumentTypes': null, 'isConstant': false, 'isLValue': false, 'isPure': false, 'isStructConstructorCall': false, 'lValueRequested': false, 'names': [null], 'type': 'bytes memory', 'type_conversion': false}, 'children': [{'attributes': {'argumentTypes': [{'typeIdentifier': 't_string_memory_ptr', 'typeString': 'string memory'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}, {'typeIdentifier': 't_uint256', 'typeString': 'uint256'}], 'isConstant': false, 'isLValue': false, 'isPure': false, 'lValueRequested': false, 'member_name': 'encodeWithSignature', 'referencedDeclaration': null, 'type': 'function (string memory) pure returns (bytes memory)'}, 'children': [{'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 64, 'type': 'abi', 'value': 'abi'}, 'id': 48, 'name': 'Identifier', 'src': '367:3:0'}], 'id': 50, 'name': 'MemberAccess', 'src': '367:23:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 11, 'type': 'string memory', 'value': 'sig'}, 'id': 51, 'name': 'Identifier', 'src': '391:3:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 7, 'type': 'uint256', 'value': 'a'}, 'id': 52, 'name': 'Identifier', 'src': '396:1:0'}, {'attributes': {'argumentTypes': null, 'overloadedDeclarations': [null], 'referencedDeclaration': 15, 'type': 'uint256', 'value': 'b'}, 'id': 53, 'name': 'Identifier', 'src': '399:1:0'}], 'id': 54, 'name': 'FunctionCall', 'src': '367:34:0'}
t.equals(common.isAbiNamespaceCall(node1), true, 'encode abi') // t.equals(common.isAbiNamespaceCall(node1), true, 'encode abi')
t.equals(common.isAbiNamespaceCall(node2), true, 'encodePacked abi') // t.equals(common.isAbiNamespaceCall(node2), true, 'encodePacked abi')
t.equals(common.isAbiNamespaceCall(node3), true, 'encodeWithSelector abi') // t.equals(common.isAbiNamespaceCall(node3), true, 'encodeWithSelector abi')
t.equals(common.isAbiNamespaceCall(node4), true, 'encodeWithSignature abi') // t.equals(common.isAbiNamespaceCall(node4), true, 'encodeWithSignature abi')
t.equals(common.isBuiltinFunctionCall(node1), true, 'encode Builtin') // t.equals(common.isBuiltinFunctionCall(node1), true, 'encode Builtin')
t.equals(common.isBuiltinFunctionCall(node2), true, 'encodePacked Builtin') // t.equals(common.isBuiltinFunctionCall(node2), true, 'encodePacked Builtin')
t.equals(common.isBuiltinFunctionCall(node3), true, 'encodeWithSelector Builtin') // t.equals(common.isBuiltinFunctionCall(node3), true, 'encodeWithSelector Builtin')
t.equals(common.isBuiltinFunctionCall(node4), true, 'encodeWithSignature Builtin') // t.equals(common.isBuiltinFunctionCall(node4), true, 'encodeWithSignature Builtin')
}) // })

@ -1,32 +1,32 @@
import { default as test} from "tape" // import { default as test} from "tape"
import { helpers } from 'remix-lib' // 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 { 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 = 'solidity-v0.4.24'
function compile (fileName) { // function compile (fileName) {
const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') // const content = 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 = 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 = 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) => {
}) // })
}, '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) => {
t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) // t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
}) // })
}) // })

@ -1,32 +1,32 @@
import { default as test} from "tape" // import { default as test} from "tape"
import { helpers } from 'remix-lib' // 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 { 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 = 'solidity-v0.5'
function compile (fileName) { // function compile (fileName) {
const content = readFileSync(join(__dirname, 'test-contracts/' + folder, fileName), 'utf8') // const content = 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 = 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 = 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) => {
}) // })
}, '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) => {
t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors')) // t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
}) // })
}) // })

Loading…
Cancel
Save