Skip to content

Commit 97c9408

Browse files
authored
Merge pull request #3892 from ethereum/ssa-improvements
SSA Improvements
2 parents e511e51 + 23e6620 commit 97c9408

File tree

5 files changed

+312
-170
lines changed

5 files changed

+312
-170
lines changed

apps/remix-ide/src/app/tabs/analysis-tab.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class AnalysisTab extends ViewPlugin {
3838
}
3939
this.dispatch = null
4040
this.hints = []
41+
this.basicEnabled = false
42+
this.solhintEnabled = false
43+
this.slitherEnabled = false
4144
}
4245

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

5053
this.event.register('staticAnaysisWarning', (count) => {
5154
let payloadType = ''
52-
const error = this.hints.find(hint => hint.type === 'error')
53-
const warning = this.hints.find(hints => hints.type === 'warning')
54-
if (error) {
55+
const error = this.hints?.find(hint => hint.type === 'error')
56+
if (error && this.solhintEnabled) {
5557
payloadType = 'error'
5658
} else {
5759
payloadType = 'warning'
5860
}
5961

6062
if (count > 0) {
61-
this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have some problem${count === 1 ? '' : 's'}` : 'You have some warnings', type: payloadType })
63+
this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have ${count} problem${count === 1 ? '' : 's'}` : `You have ${count} warnings`, type: payloadType })
6264
} else if (count === 0) {
6365
this.emit('statusChanged', { key: 'succeed', title: 'no warnings or errors', type: 'success' })
6466
} else {

libs/remix-ui/static-analyser/src/lib/ErrorRenderer.tsx

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
import { CustomTooltip } from '@remix-ui/helper';
2-
import React from 'react' //eslint-disable-line
1+
import { CustomTooltip } from "@remix-ui/helper"
2+
import React from "react"; //eslint-disable-line
3+
import { RemixUiStaticAnalyserState } from "../staticanalyser"
34

45
interface ErrorRendererProps {
56
message: any;
6-
opt: any,
7-
warningErrors: any
8-
editor: any,
9-
name: string,
7+
opt: any;
8+
warningErrors: any;
9+
editor: any;
10+
name: string;
11+
ssaState: RemixUiStaticAnalyserState
1012
}
1113

12-
const ErrorRenderer = ({ message, opt, editor, name }: ErrorRendererProps) => {
14+
const ErrorRenderer = ({ message, opt, editor, name, ssaState }: ErrorRendererProps) => {
1315
const getPositionDetails = (msg: any) => {
14-
const result = { } as Record<string, number | string>
16+
const result = {} as Record<string, number | string>
1517

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

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

2426
// extract file
2527
position = msg.match(/^(https:.*?|http:.*?|.*?):/)
26-
result.errFile = position ? position[1] : ''
27-
return result
28+
result.errFile = position ? position[1] : ""
29+
return result;
2830
}
2931

3032
const handlePointToErrorOnClick = async (location, fileName) => {
31-
await editor.call('editor', 'discardHighlight')
32-
await editor.call('editor', 'highlight', location, fileName, '', { focus: true })
33+
await editor.call("editor", "discardHighlight")
34+
await editor.call("editor", "highlight", location, fileName, "", {
35+
focus: true,
36+
})
3337
}
3438

3539
if (!message) return
3640
let position = getPositionDetails(message)
37-
if (!position.errFile || (opt.errorType && opt.errorType === position.errFile)) {
41+
if (
42+
!position.errFile ||
43+
(opt.errorType && opt.errorType === position.errFile)
44+
) {
3845
// Updated error reported includes '-->' before file details
39-
const errorDetails = message.split('-->')
46+
const errorDetails = message.split("-->")
4047
// errorDetails[1] will have file details
4148
if (errorDetails.length > 1) position = getPositionDetails(errorDetails[1])
4249
}
4350
opt.errLine = position.errLine
4451
opt.errCol = position.errCol
4552
opt.errFile = position.errFile.trim()
46-
const classList = opt.type === 'error' ? 'alert alert-danger' : 'alert alert-warning'
53+
const classList =
54+
opt.type === "error" ? "alert alert-danger" : "alert alert-warning"
4755
return (
4856
<div>
4957
<div className={`sol ${opt.type} ${classList}`}>
50-
<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' }}>
51-
<span className='h6 font-weight-bold'>{opt.name}</span>
52-
<span>{ opt.item.warning }</span>
53-
{opt.item.more
54-
? <span><a href={opt.item.more} target='_blank'>more</a></span>
55-
: <span> </span>
58+
<div
59+
className="d-flex flex-column"
60+
data-id={`${name}Button`}
61+
onClick={async () =>
62+
await handlePointToErrorOnClick(opt.location, opt.fileName)
5663
}
57-
<CustomTooltip
58-
placement="right"
59-
tooltipId="errorTooltip"
60-
tooltipText={`Position in ${opt.errFile}`}
61-
tooltipClasses="text-nowrap"
62-
>
63-
<span>Pos: {opt.locationString}</span>
64-
</CustomTooltip>
65-
</span>
64+
style={{
65+
cursor: "pointer",
66+
overflow: "hidden",
67+
textOverflow: "ellipsis",
68+
}}
69+
>
70+
<span className="h6 font-weight-bold">{opt.name}</span>
71+
<span>{opt.item.warning}</span>
72+
{opt.item.more ? (
73+
<span>
74+
<a href={opt.item.more} target="_blank">
75+
more
76+
</a>
77+
</span>
78+
) : (
79+
<span> </span>
80+
)}
81+
<div>
82+
<CustomTooltip
83+
placement="right"
84+
tooltipId="errorTooltip"
85+
tooltipText={`Position in ${ssaState.file}`}
86+
tooltipClasses="text-nowrap"
87+
>
88+
<span>Pos: {opt.locationString}</span>
89+
</CustomTooltip>
90+
</div>
91+
</div>
6692
</div>
6793
</div>
6894
)

libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -56,73 +56,82 @@ slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<b
5656
const warningErrors = []
5757
props.analysisModule.hints = []
5858
// Run solhint
59+
if (solhintEnabled) {
5960
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'solHint'])
6061
const hintsResult = await props.analysisModule.call('solhint', 'lint', state.file)
61-
props.analysisModule.hints = solhintEnabled === false ? 0 : hintsResult
62+
props.analysisModule.hints = hintsResult
6263
setHints(hintsResult)
64+
} else {
65+
props.analysisModule.hints = []
66+
setHints([])
67+
}
6368
// Remix Analysis
64-
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer'])
65-
const results = runner.run(lastCompilationResult, categoryIndex)
66-
for (const result of results) {
67-
let moduleName
68-
Object.keys(groupedModules).map(key => {
69-
groupedModules[key].forEach(el => {
70-
if (el.name === result.name) {
71-
moduleName = groupedModules[key][0].categoryDisplayName
72-
}
69+
if (basicEnabled) {
70+
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer'])
71+
const results = runner.run(lastCompilationResult, categoryIndex)
72+
for (const result of results) {
73+
let moduleName
74+
Object.keys(groupedModules).map(key => {
75+
groupedModules[key].forEach(el => {
76+
if (el.name === result.name) {
77+
moduleName = groupedModules[key][0].categoryDisplayName
78+
}
79+
})
7380
})
74-
})
75-
// iterate over the warnings and create an object
76-
for (const item of result.report) {
77-
let location: any = {}
78-
let locationString = 'not available'
79-
let column = 0
80-
let row = 0
81-
let fileName = currentFile
82-
let isLibrary = false
81+
// iterate over the warnings and create an object
82+
for (const item of result.report) {
83+
let location: any = {}
84+
let locationString = 'not available'
85+
let column = 0
86+
let row = 0
87+
let fileName = currentFile
88+
let isLibrary = false
8389

84-
if (item.location) {
85-
const split = item.location.split(':')
86-
const file = split[2]
87-
location = {
88-
start: parseInt(split[0]),
89-
length: parseInt(split[1])
90+
if (item.location) {
91+
const split = item.location.split(':')
92+
const file = split[2]
93+
location = {
94+
start: parseInt(split[0]),
95+
length: parseInt(split[1])
96+
}
97+
location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
98+
location,
99+
parseInt(file),
100+
lastCompilationSource.sources,
101+
lastCompilationResult.sources
102+
)
103+
row = location.start.line
104+
column = location.start.column
105+
locationString = row + 1 + ':' + column + ':'
106+
fileName = Object.keys(lastCompilationResult.sources)[file]
90107
}
91-
location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
92-
location,
93-
parseInt(file),
94-
lastCompilationSource.sources,
95-
lastCompilationResult.sources
96-
)
97-
row = location.start.line
98-
column = location.start.column
99-
locationString = row + 1 + ':' + column + ':'
100-
fileName = Object.keys(lastCompilationResult.sources)[file]
101-
}
102-
if(fileName !== currentFile) {
103-
const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
104-
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
105-
}
106-
const msg = message(result.name, item.warning, item.more, fileName, locationString)
107-
const options = {
108-
type: 'warning',
109-
useSpan: true,
110-
errFile: fileName,
111-
fileName,
112-
isLibrary,
113-
errLine: row,
114-
errCol: column,
115-
item: item,
116-
name: result.name,
117-
locationString,
118-
more: item.more,
119-
location: location
108+
if (fileName !== currentFile) {
109+
const { file, provider } = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
110+
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
111+
}
112+
const msg = message(result.name, item.warning, item.more, state.file, locationString)
113+
const options = {
114+
type: 'warning',
115+
useSpan: true,
116+
errFile: state.file,
117+
fileName,
118+
isLibrary,
119+
errLine: row,
120+
errCol: column,
121+
item: item,
122+
name: result.name,
123+
locationString,
124+
more: item.more,
125+
location: location
126+
}
127+
warningErrors.push(options)
128+
warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
129+
setSsaWarnings(warningMessage)
120130
}
121-
warningErrors.push(options)
122-
warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
123-
setSsaWarnings(warningMessage)
124131
}
125-
}
132+
} else {
133+
setSsaWarnings([])
134+
}
126135
// Slither Analysis
127136
if (showSlither && slitherEnabled) {
128137
setSlitherWarnings([])
@@ -198,12 +207,12 @@ slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<b
198207
props.analysisModule.call('terminal', 'log', { type: 'error', value: '[Slither Analysis]: Error occured! See remixd console for details.' })
199208
showWarnings(warningMessage, 'warningModuleName')
200209
}
201-
} else showWarnings(warningMessage, 'warningModuleName')
202-
setStartAnalysis(false)
210+
} else setStartAnalysis(false)
203211
} else {
204212
if (categoryIndex.length) {
205213
warningContainer.current.innerText = 'No compiled AST available'
206214
}
215+
207216
props.event.trigger('staticAnaysisWarning', [-1])
208217
}
209218
}

0 commit comments

Comments
 (0)