Commit 0569590c authored by aniket-engg's avatar aniket-engg Committed by Aniket

modules updated for current AST

parent b6b07a0c
...@@ -18,7 +18,7 @@ export default class staticAnalysisRunner { ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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
......
'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 ...@@ -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) { ...@@ -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 ...@@ -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) { ...@@ -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.`)
if(current !== null)
return current return current
} }
...@@ -15,7 +15,7 @@ export default class gasCosts { ...@@ -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' ...@@ -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 { ...@@ -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 { ...@@ -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' ...@@ -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 { ...@@ -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' ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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
} }
......
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'))
}) // })
}) // })
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'))
}) // })
}) // })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment