Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSA Improvements #3892

Merged
merged 14 commits into from
Jul 6, 2023
10 changes: 6 additions & 4 deletions apps/remix-ide/src/app/tabs/analysis-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class AnalysisTab extends ViewPlugin {
}
this.dispatch = null
this.hints = []
this.basicEnabled = false
this.solhintEnabled = false
this.slitherEnabled = false
}

async onActivation () {
Expand All @@ -49,16 +52,15 @@ class AnalysisTab extends ViewPlugin {

this.event.register('staticAnaysisWarning', (count) => {
let payloadType = ''
const error = this.hints.find(hint => hint.type === 'error')
const warning = this.hints.find(hints => hints.type === 'warning')
if (error) {
const error = this.hints?.find(hint => hint.type === 'error')
if (error && this.solhintEnabled) {
payloadType = 'error'
} else {
payloadType = 'warning'
}

if (count > 0) {
this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have some problem${count === 1 ? '' : 's'}` : 'You have some warnings', type: payloadType })
this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have ${count} problem${count === 1 ? '' : 's'}` : `You have ${count} warnings`, type: payloadType })
} else if (count === 0) {
this.emit('statusChanged', { key: 'succeed', title: 'no warnings or errors', type: 'success' })
} else {
Expand Down
88 changes: 57 additions & 31 deletions libs/remix-ui/static-analyser/src/lib/ErrorRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { CustomTooltip } from '@remix-ui/helper';
import React from 'react' //eslint-disable-line
import { CustomTooltip } from "@remix-ui/helper"
import React from "react"; //eslint-disable-line
import { RemixUiStaticAnalyserState } from "../staticanalyser"

interface ErrorRendererProps {
message: any;
opt: any,
warningErrors: any
editor: any,
name: string,
opt: any;
warningErrors: any;
editor: any;
name: string;
ssaState: RemixUiStaticAnalyserState
}

const ErrorRenderer = ({ message, opt, editor, name }: ErrorRendererProps) => {
const ErrorRenderer = ({ message, opt, editor, name, ssaState }: ErrorRendererProps) => {
const getPositionDetails = (msg: any) => {
const result = { } as Record<string, number | string>
const result = {} as Record<string, number | string>

// To handle some compiler warning without location like SPDX license warning etc
if (!msg.includes(':')) return { errLine: -1, errCol: -1, errFile: msg }
if (!msg.includes(":")) return { errLine: -1, errCol: -1, errFile: msg }

// extract line / column
let position = msg.match(/^(.*?):([0-9]*?):([0-9]*?)?/)
Expand All @@ -23,46 +25,70 @@ const ErrorRenderer = ({ message, opt, editor, name }: ErrorRendererProps) => {

// extract file
position = msg.match(/^(https:.*?|http:.*?|.*?):/)
result.errFile = position ? position[1] : ''
return result
result.errFile = position ? position[1] : ""
return result;
}

const handlePointToErrorOnClick = async (location, fileName) => {
await editor.call('editor', 'discardHighlight')
await editor.call('editor', 'highlight', location, fileName, '', { focus: true })
await editor.call("editor", "discardHighlight")
await editor.call("editor", "highlight", location, fileName, "", {
focus: true,
})
}

if (!message) return
let position = getPositionDetails(message)
if (!position.errFile || (opt.errorType && opt.errorType === position.errFile)) {
if (
!position.errFile ||
(opt.errorType && opt.errorType === position.errFile)
) {
// Updated error reported includes '-->' before file details
const errorDetails = message.split('-->')
const errorDetails = message.split("-->")
// errorDetails[1] will have file details
if (errorDetails.length > 1) position = getPositionDetails(errorDetails[1])
}
opt.errLine = position.errLine
opt.errCol = position.errCol
opt.errFile = position.errFile.trim()
const classList = opt.type === 'error' ? 'alert alert-danger' : 'alert alert-warning'
const classList =
opt.type === "error" ? "alert alert-danger" : "alert alert-warning"
return (
<div>
<div className={`sol ${opt.type} ${classList}`}>
<span className='d-flex flex-column' data-id={`${name}Button`} onClick={async () => await handlePointToErrorOnClick(opt.location, opt.fileName)} style={{ cursor: "pointer", overflow: 'hidden', textOverflow: 'ellipsis' }}>
<span className='h6 font-weight-bold'>{opt.name}</span>
<span>{ opt.item.warning }</span>
{opt.item.more
? <span><a href={opt.item.more} target='_blank'>more</a></span>
: <span> </span>
<div
className="d-flex flex-column"
data-id={`${name}Button`}
onClick={async () =>
await handlePointToErrorOnClick(opt.location, opt.fileName)
}
<CustomTooltip
placement="right"
tooltipId="errorTooltip"
tooltipText={`Position in ${opt.errFile}`}
tooltipClasses="text-nowrap"
>
<span>Pos: {opt.locationString}</span>
</CustomTooltip>
</span>
style={{
cursor: "pointer",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
<span className="h6 font-weight-bold">{opt.name}</span>
<span>{opt.item.warning}</span>
{opt.item.more ? (
<span>
<a href={opt.item.more} target="_blank">
more
</a>
</span>
) : (
<span> </span>
)}
<div>
<CustomTooltip
placement="right"
tooltipId="errorTooltip"
tooltipText={`Position in ${ssaState.file}`}
tooltipClasses="text-nowrap"
>
<span>Pos: {opt.locationString}</span>
</CustomTooltip>
</div>
</div>
</div>
</div>
)
Expand Down
129 changes: 69 additions & 60 deletions libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,73 +56,82 @@ slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<b
const warningErrors = []
props.analysisModule.hints = []
// Run solhint
if (solhintEnabled) {
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'solHint'])
const hintsResult = await props.analysisModule.call('solhint', 'lint', state.file)
props.analysisModule.hints = solhintEnabled === false ? 0 : hintsResult
props.analysisModule.hints = hintsResult
setHints(hintsResult)
} else {
props.analysisModule.hints = []
setHints([])
}
// Remix Analysis
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer'])
const results = runner.run(lastCompilationResult, categoryIndex)
for (const result of results) {
let moduleName
Object.keys(groupedModules).map(key => {
groupedModules[key].forEach(el => {
if (el.name === result.name) {
moduleName = groupedModules[key][0].categoryDisplayName
}
if (basicEnabled) {
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer'])
const results = runner.run(lastCompilationResult, categoryIndex)
for (const result of results) {
let moduleName
Object.keys(groupedModules).map(key => {
groupedModules[key].forEach(el => {
if (el.name === result.name) {
moduleName = groupedModules[key][0].categoryDisplayName
}
})
})
})
// iterate over the warnings and create an object
for (const item of result.report) {
let location: any = {}
let locationString = 'not available'
let column = 0
let row = 0
let fileName = currentFile
let isLibrary = false
// iterate over the warnings and create an object
for (const item of result.report) {
let location: any = {}
let locationString = 'not available'
let column = 0
let row = 0
let fileName = currentFile
let isLibrary = false

if (item.location) {
const split = item.location.split(':')
const file = split[2]
location = {
start: parseInt(split[0]),
length: parseInt(split[1])
if (item.location) {
const split = item.location.split(':')
const file = split[2]
location = {
start: parseInt(split[0]),
length: parseInt(split[1])
}
location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
location,
parseInt(file),
lastCompilationSource.sources,
lastCompilationResult.sources
)
row = location.start.line
column = location.start.column
locationString = row + 1 + ':' + column + ':'
fileName = Object.keys(lastCompilationResult.sources)[file]
}
location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
location,
parseInt(file),
lastCompilationSource.sources,
lastCompilationResult.sources
)
row = location.start.line
column = location.start.column
locationString = row + 1 + ':' + column + ':'
fileName = Object.keys(lastCompilationResult.sources)[file]
}
if(fileName !== currentFile) {
const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
}
const msg = message(result.name, item.warning, item.more, fileName, locationString)
const options = {
type: 'warning',
useSpan: true,
errFile: fileName,
fileName,
isLibrary,
errLine: row,
errCol: column,
item: item,
name: result.name,
locationString,
more: item.more,
location: location
if (fileName !== currentFile) {
const { file, provider } = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
}
const msg = message(result.name, item.warning, item.more, state.file, locationString)
const options = {
type: 'warning',
useSpan: true,
errFile: state.file,
fileName,
isLibrary,
errLine: row,
errCol: column,
item: item,
name: result.name,
locationString,
more: item.more,
location: location
}
warningErrors.push(options)
warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
setSsaWarnings(warningMessage)
}
warningErrors.push(options)
warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
setSsaWarnings(warningMessage)
}
}
} else {
setSsaWarnings([])
}
// Slither Analysis
if (showSlither && slitherEnabled) {
setSlitherWarnings([])
Expand Down Expand Up @@ -198,12 +207,12 @@ slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<b
props.analysisModule.call('terminal', 'log', { type: 'error', value: '[Slither Analysis]: Error occured! See remixd console for details.' })
showWarnings(warningMessage, 'warningModuleName')
}
} else showWarnings(warningMessage, 'warningModuleName')
setStartAnalysis(false)
} else setStartAnalysis(false)
} else {
if (categoryIndex.length) {
warningContainer.current.innerText = 'No compiled AST available'
}

props.event.trigger('staticAnaysisWarning', [-1])
}
}
Expand Down
Loading