Merge pull request #3892 from ethereum/ssa-improvements

SSA Improvements
pull/3876/head
Joseph Izang 2 years ago committed by GitHub
commit 97c9408e9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      apps/remix-ide/src/app/tabs/analysis-tab.js
  2. 88
      libs/remix-ui/static-analyser/src/lib/ErrorRenderer.tsx
  3. 129
      libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
  4. 252
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  5. 3
      libs/remix-ui/static-analyser/src/staticanalyser.d.ts

@ -38,6 +38,9 @@ class AnalysisTab extends ViewPlugin {
}
this.dispatch = null
this.hints = []
this.basicEnabled = false
this.solhintEnabled = false
this.slitherEnabled = false
}
async onActivation () {
@ -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 {

@ -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]*?)?/)
@ -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>
)

@ -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([])
@ -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])
}
}

@ -29,6 +29,7 @@ import {
calculateWarningStateEntries,
} from "./components/BasicTitle";
import { Nav, TabContainer } from "react-bootstrap";
import { CustomTooltip } from "@remix-ui/helper";
declare global {
interface Window {
@ -365,8 +366,8 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
onChange={() => {}}
/>
</div>
)
}
);
};
const categorySection = (category, categoryId, i) => {
return (
@ -416,8 +417,8 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
</TreeView>
</div>
</div>
)
}
);
};
useEffect(() => {
if (!hideWarnings && !showLibsWarning) {
@ -436,85 +437,95 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
props.event.trigger("staticAnaysisWarning", [
slitherTotal + ssaTotal + hintsTotal === 0
? -1
: slitherTotal + ssaTotal + hintsTotal,
])
: !solhintEnabled && !basicEnabled && !slitherEnabled ? -1 : slitherTotal + ssaTotal + hintsTotal,
]);
}
if (!hideWarnings && showLibsWarning) {
props.event.trigger("staticAnaysisWarning", [
slitherWarnings.length + ssaWarnings.length + hints.length === 0
? -1
: slitherWarnings.length + ssaWarnings.length + hints.length,
])
: !solhintEnabled && !basicEnabled && !slitherEnabled ? -1 : slitherWarnings.length + ssaWarnings.length + hints.length,
]);
}
if (hideWarnings) {
const slitherTotal =
slitherWarnings && state.data && state.source !== null
? slitherWarnings.filter((x) => x.options.type === "error").length
: 0
: 0;
const ssaTotal =
ssaWarnings && state.data && state.source !== null
? ssaWarnings.filter((x) => x.options.type === "error").length
: 0
: 0;
const hintsTotal =
hints && state.data && state.source !== null
? hints.filter((x) => x.type === "error").length
: 0
: 0;
props.event.trigger("staticAnaysisWarning", [
slitherTotal + ssaTotal + hintsTotal === 0
? -1
: slitherTotal + ssaTotal + hintsTotal,
])
]);
}
if (hideWarnings && !solhintEnabled && !slitherEnabled && !basicEnabled) {
props.event.trigger("staticAnaysisWarning", [-1])
}
}, [hideWarnings, showLibsWarning])
useEffect(() => {
const slitherTotal =
slitherEnabled &&
let slitherTotal = 0
if (slitherEnabled &&
showSlither &&
slitherWarnings &&
state.data &&
state.source !== null
? slitherWarnings.filter((x) => !x.options.isLibrary && x.hasWarning)
.length
: 0
const ssaTotal =
basicEnabled && ssaWarnings && state.data && state.source !== null
? ssaWarnings.filter((x) => !x.options.isLibrary && x.hasWarning).length
: 0
const hintsTotal =
solhintEnabled && hints && state.data && state.source !== null
? hints.length
: 0
state.source !== null) {
slitherTotal = slitherWarnings.filter((x) => !x.options.isLibrary && x.hasWarning).length
props.analysisModule.slitherEnabled = true
}
let ssaTotal = 0
if (basicEnabled && ssaWarnings && state.data && state.source !== null) {
ssaTotal = ssaWarnings.filter((x) => !x.options.isLibrary && x.hasWarning).length
props.analysisModule.basicEnabled = true
}
let hintsTotal = 0
if (solhintEnabled && hints && state.data && state.source !== null) {
hintsTotal = hints.length
props.analysisModule.solhintEnabled = true
}
props.event.trigger("staticAnaysisWarning", [
slitherTotal + ssaTotal + hintsTotal === 0
? -1
: slitherTotal + ssaTotal + hintsTotal,
])
]);
}, [hints.length, slitherWarnings.length, ssaWarnings.length])
useEffect(() => {
const slitherTotal =
slitherWarnings &&
let slitherTotal = 0
if (slitherWarnings &&
slitherEnabled &&
showSlither &&
state.data &&
state.source !== null
? slitherWarnings.filter((x) => !x.options.isLibrary && x.hasWarning)
.length
: 0
const ssaTotal =
ssaWarnings && basicEnabled && state.data && state.source !== null
? ssaWarnings.filter((x) => !x.options.isLibrary && x.hasWarning).length
: 0
const hintsTotal =
hints && solhintEnabled && state.data && state.source !== null
? hints.length
: 0
state.source !== null) {
slitherTotal = slitherWarnings.filter((x) => !x.options.isLibrary && x.hasWarning)
.length
props.analysisModule.slitherEnabled = true
}
let ssaTotal = 0
if (ssaWarnings && basicEnabled && state.data && state.source !== null) {
ssaTotal = ssaWarnings.filter((x) => !x.options.isLibrary && x.hasWarning).length
props.analysisModule.basicEnabled = true
}
let hintsTotal = 0
if (hints && solhintEnabled && state.data && state.source !== null) {
hintsTotal = hints.length
}
props.event.trigger("staticAnaysisWarning", [
slitherTotal + ssaTotal + hintsTotal === 0
? -1
: slitherTotal + ssaTotal + hintsTotal,
]);
])
}, [solhintEnabled, basicEnabled, slitherEnabled, showSlither])
const handleSlitherEnabled = async () => {
@ -524,36 +535,42 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
"remixd"
);
if (showSlither) {
setShowSlither(false);
setShowSlither(false)
props.analysisModule.slitherEnabled = false
}
if (!showSlither) {
setShowSlither(true);
setShowSlither(true)
props.analysisModule.slitherEnabled = true
}
};
const handleBasicEnabled = () => {
if (basicEnabled) {
setBasicEnabled(false);
setBasicEnabled(false)
props.analysisModule.basicEnabled = false
if (solhintEnabled) {
setSelectedTab("solhint");
setSelectedTab("solhint")
}
props.event.trigger("staticAnalysisWarning", [-1]);
props.event.trigger("staticAnalysisWarning", [-1])
} else {
setBasicEnabled(true);
props.event.trigger("staticAnalysisWarning", [-1]);
setBasicEnabled(true)
props.analysisModule.basicEnabled = true
props.event.trigger("staticAnalysisWarning", [-1])
}
};
const handleLinterEnabled = () => {
if (solhintEnabled) {
setSolhintEnabled(false);
props.analysisModule.solhintEnabled = false
if (basicEnabled) {
setSelectedTab("remix");
setSelectedTab("remix")
}
props.event.trigger("staticAnalysisWarning", [-1]);
props.event.trigger("staticAnalysisWarning", [-1])
} else {
setSolhintEnabled(true);
props.event.trigger("staticAnalysisWarning", [-1]);
setSolhintEnabled(true)
props.analysisModule.solhintEnabled = true
props.event.trigger("staticAnalysisWarning", [-1])
}
};
@ -623,11 +640,70 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
{hint.formattedMessage}
</span>
<br />
<span>{`${hint.column}:${hint.line}`}</span>
<CustomTooltip
placement="right"
tooltipId="errorTooltip"
tooltipText={`Position in ${state.file}`}
tooltipClasses="text-nowrap"
>
<span>{`Pos: ${hint.column}:${hint.line}`}</span>
</CustomTooltip>
</div>
</div>
))
: hintErrors.map((hint, index) => (
: !hideWarnings && !showLibsWarning && !basicEnabled && solhintEnabled ? hints.map((hint, index) => (
<div
key={index}
className={`${
hint.type === "warning"
? "alert alert-warning"
: "alert alert-danger"
}`}
style={{
cursor: "pointer",
overflow: "hidden",
textOverflow: "ellipsis",
}}
onClick={async () => {
await props.analysisModule.call(
"editor",
"discardHighlight"
);
await props.analysisModule.call(
"editor",
"highlight",
{
end: {
line: hint.line,
column: hint.column + 1,
},
start: {
line: hint.line,
column: hint.column,
},
},
state.file,
"",
{ focus: true }
);
}}
>
<div>
<span className="text-wrap">
{hint.formattedMessage}
</span>
<br />
<CustomTooltip
placement="right"
tooltipId="errorTooltip"
tooltipText={`Position in ${state.file}`}
tooltipClasses="text-nowrap"
>
<span>{`Pos: ${hint.column}:${hint.line}`}</span>
</CustomTooltip>
</div>
</div>
)) : hintErrors.map((hint, index) => (
<div
key={index}
className="alert alert-danger"
@ -671,7 +747,14 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
{hint.formattedMessage}
</span>
<br />
<span>{`${hint.column}:${hint.line}`}</span>
<CustomTooltip
placement="right"
tooltipId="errorTooltip"
tooltipText={`Position in ${state.file}`}
tooltipClasses="text-nowrap"
>
<span>{`Pos: ${hint.column}:${hint.line}`}</span>
</CustomTooltip>
</div>
</div>
))}
@ -733,7 +816,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
),
child: (
<>
{Object.entries(warningState).length > 0 ? (
{ssaWarnings.length > 0 ? (
<div id="staticanalysisresult">
<div className="mb-4 pt-2">
<div>
@ -751,6 +834,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
message={x.msg}
opt={x.options}
warningErrors={""}
ssaState={state}
editor={props.analysisModule}
/>
</div>
@ -767,11 +851,29 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
message={x.msg}
opt={x.options}
warningErrors={""}
ssaState={state}
editor={props.analysisModule}
/>
</div>
))
: null}
: !hideWarnings && !showLibsWarning && basicEnabled
? ssaWarnings.filter((x) => !x.options.isLibrary && x.hasWarning)
.map((x, i) => (
<div
data-id={`staticAnalysisModule${x.warningModuleName}${i}`}
id={`staticAnalysisModule${x.warningModuleName}${i}`}
key={i}
>
<ErrorRenderer
name={`staticAnalysisModule${x.warningModuleName}${i}`}
message={x.msg}
opt={x.options}
warningErrors={""}
ssaState={state}
editor={props.analysisModule}
/>
</div>
)) : null}
</div>
</div>
</div>
@ -838,6 +940,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
name={`staticAnalysisModule${warning.warningModuleName}${index}`}
message={warning.msg}
opt={warning.options}
ssaState={state}
warningErrors={""}
editor={props.analysisModule}
/>
@ -854,6 +957,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
message={warning.msg}
opt={warning.options}
warningErrors={""}
ssaState={state}
editor={props.analysisModule}
/>
</div>
@ -870,6 +974,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
name={`staticAnalysisModule${warning.warningModuleName}${index}`}
message={warning.msg}
opt={warning.options}
ssaState={state}
warningErrors={""}
editor={props.analysisModule}
/>
@ -919,7 +1024,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
.map((value: any) => {
return value.map((x) => {
return x._index.toString();
})
});
})
.flat()
.every((el) => categoryIndex.includes(el))
@ -1007,7 +1112,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
buttonText={`Analyze ${state.file}`}
title={`${runButtonTitle}`}
classList="btn btn-sm btn-primary btn-block"
onClick={async () =>
onClick={async () => {
await run(
state.data,
state.source,
@ -1035,7 +1140,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
solhintEnabled,
basicEnabled
)
}
}}
disabled={
state.data === null ||
!isSupportedVersion ||
@ -1043,10 +1148,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
}
/>
)}
{state &&
state.data !== null &&
state.source !== null &&
state.file.length > 0 ? (
{ssaWarnings.length > 0 || hints.length > 0 ? (
<div className="d-flex border-top flex-column">
{slitherWarnings.length > 0 ||
hints.length > 0 ||
@ -1070,7 +1172,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
</div>
) : null}
<div
className="border-top mt-2 pt-2 mb-2"
className="border-top mt-2 pt-2 mb-3"
id="staticanalysisresult"
>
<RemixUiCheckbox
@ -1089,7 +1191,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
name="hideWarnings"
inputType="checkbox"
checked={hideWarnings}
label="Hide warnings"
label="Show errors only"
onClick={handleHideWarnings}
onChange={() => {}}
/>
@ -1101,7 +1203,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
setSelectedTab(tabKey);
}}
>
<Nav variant="tabs" className="px-1">
<Nav variant="tabs">
{checkBasicStatus() ? (
<Nav.Item>
<Nav.Link
@ -1113,7 +1215,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
</Nav.Item>
) : null}
{solhintEnabled ? (
<Nav.Item className="text-decoration-none font-weight-bold px-2">
<Nav.Item>
<Nav.Link
className="text-decoration-none font-weight-bold px-2"
eventKey={tabKeys[0].tabKey}
@ -1123,7 +1225,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
</Nav.Item>
) : null}
{slitherEnabled && showSlither ? (
<Nav.Item className="text-decoration-none font-weight-bold px-2">
<Nav.Item>
<Nav.Link
className="text-decoration-none font-weight-bold px-2"
eventKey={tabKeys[2].tabKey}
@ -1136,13 +1238,13 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
<Tab.Content>
<Tab.Pane eventKey={tabKeys[1].tabKey}>
{tabKeys[1].child}
{basicEnabled ? tabKeys[1].child : null}
</Tab.Pane>
<Tab.Pane eventKey={tabKeys[0].tabKey}>
{tabKeys[0].child}
{solhintEnabled ? tabKeys[0].child : null}
</Tab.Pane>
<Tab.Pane eventKey={tabKeys[2].tabKey}>
{tabKeys[2].child}
{slitherEnabled && showSlither ? tabKeys[2].child : null}
</Tab.Pane>
</Tab.Content>
</TabContainer>
@ -1151,7 +1253,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
</div>
</div>
</div>
);
};
)
}
export default RemixUiStaticAnalyser;

@ -12,6 +12,9 @@ export declare class AnalysisTab extends ViewPlugin {
column: number;
line: number;
}[]
basicEnabled: boolean;
solhintEnabled: boolean;
slitherEnabled: boolean;
internalCount: number
registry: Registry;
element: HTMLDivElement;

Loading…
Cancel
Save