Merge pull request #3838 from ethereum/ssa-qa-fixes

Ssa Qa Fixes
0.34.0
yann300 1 year ago committed by GitHub
commit bd494e84f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts
  2. 14
      apps/remix-ide/src/app/tabs/analysis-tab.js
  3. 8
      apps/solhint/src/app/SolhintPluginClient.ts
  4. 12
      libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts
  5. 21
      libs/remix-ui/static-analyser/src/lib/components/BasicTitle.tsx
  6. 10
      libs/remix-ui/static-analyser/src/lib/reducers/staticAnalysisReducer.ts
  7. 1262
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  8. 46
      libs/remix-ui/static-analyser/src/staticanalyser.d.ts
  9. 38
      libs/remix-ui/vertical-icons-panel/src/lib/components/Badge.tsx

@ -11,7 +11,7 @@ contract test1 { address test = tx.origin; }
contract test2 {} contract test2 {}
contract TooMuchGas { contract TooMuchGas {
uint x; uint x;
fallback() external { fallback() external {
x++; x++;
uint test; uint test;
uint test1; uint test1;
@ -43,18 +43,18 @@ module.exports = {
.click('*[id="staticAnalysisRunBtn"]') .click('*[id="staticAnalysisRunBtn"]')
.waitForElementPresent('#staticanalysisresult .warning', 5000) .waitForElementPresent('#staticanalysisresult .warning', 5000)
// Check warning count // Check warning count
.click('*[data-rb-event-key="basic"]') .click('*[data-rb-event-key="remix"]')
.assert.containsText('*[data-id="StaticAnalysisErrorCount"]', '1') .assert.containsText('span#ssaRemixtab > *[data-id="RemixStaticAnalysisErrorCount"]', '1')
.verify.elementPresent('input[name="showLibWarnings"]') .verify.elementPresent('input[name="showLibWarnings"]')
.verify.not.elementPresent('input[name="showLibWarnings"]:checked') .verify.not.elementPresent('input[name="showLibWarnings"]:checked')
.verify.elementPresent('label[id="headingshowLibWarnings"]') .verify.elementPresent('label[id="headingshowLibWarnings"]')
.click('label[id="headingshowLibWarnings"]') .click('label[id="headingshowLibWarnings"]')
.pause(1000) .pause(1000)
.click('*[data-rb-event-key="basic"]') .click('*[data-rb-event-key="remix"]')
.assert.containsText('*[data-id="StaticAnalysisErrorCount"]', '382') .assert.containsText('span#ssaRemixtab > *[data-id="RemixStaticAnalysisErrorCount', '382')
.click('label[id="headingshowLibWarnings"]') .click('label[id="headingshowLibWarnings"]')
.pause(1000) .pause(1000)
.assert.containsText('*[data-id="StaticAnalysisErrorCount"]', '1') .assert.containsText('span#ssaRemixtab > *[data-id="RemixStaticAnalysisErrorCount', '1')
.end() .end()
} }
} }

@ -37,6 +37,7 @@ class AnalysisTab extends ViewPlugin {
'offsettolinecolumnconverter').api 'offsettolinecolumnconverter').api
} }
this.dispatch = null this.dispatch = null
this.hints = []
} }
async onActivation () { async onActivation () {
@ -47,10 +48,19 @@ class AnalysisTab extends ViewPlugin {
} }
this.event.register('staticAnaysisWarning', (count) => { 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) {
payloadType = 'error'
} else {
payloadType = 'warning'
}
if (count > 0) { if (count > 0) {
this.emit('statusChanged', { key: count, title: `${count} warning${count === 1 ? '' : 's'}`, type: 'warning' }) this.emit('statusChanged', { key: count, title: payloadType === 'error' ? `You have some problem${count === 1 ? '' : 's'}` : 'You have some warnings', type: payloadType })
} else if (count === 0) { } else if (count === 0) {
this.emit('statusChanged', { key: 'succeed', title: 'no warning', type: 'success' }) this.emit('statusChanged', { key: 'succeed', title: 'no warnings or errors', type: 'success' })
} else { } else {
// count ==-1 no compilation result // count ==-1 no compilation result
this.emit('statusChanged', { key: 'none' }) this.emit('statusChanged', { key: 'none' })

@ -85,10 +85,14 @@ export class SolHint extends PluginClient {
const reports: Array<Report> = reporters.reports const reports: Array<Report> = reporters.reports
const hints = reports.map((report: Report) => { const hints = reports.map((report: Report) => {
return { return {
formattedMessage: `${report.message}\n${report.fix ? report.fix : ''}`, formattedMessage: `${report.message}\n ${report.fix ? report.fix : ''}`,
type: this.severity[report.severity] || 'error', type: this.severity[report.severity] || 'error',
column: report.column, column: report.column,
line: report.line - 1 line: report.line - 1,
message: report.message,
ruleId: report.ruleId,
severity: report.severity,
fix: report.fix
} }
}) })

@ -44,24 +44,22 @@ export const compilation = (analysisModule: AnalysisTab,
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function run (lastCompilationResult, lastCompilationSource, currentFile: string, state: RemixUiStaticAnalyserState, props: RemixUiStaticAnalyserProps, isSupportedVersion, showSlither, categoryIndex: number[], groupedModules, runner, _paq, message, showWarnings, allWarnings: React.RefObject<any>, warningContainer: React.RefObject<any>, calculateWarningStateEntries: (e:[string, any][]) => {length: number, errors: any[] }, warningState, setHints: React.Dispatch<React.SetStateAction<SolHintReport[]>>, hints: SolHintReport[], setSlitherWarnings: React.Dispatch<React.SetStateAction<any[]>>, setSsaWarnings: React.Dispatch<React.SetStateAction<any[]>>, export async function run (lastCompilationResult, lastCompilationSource, currentFile: string, state: RemixUiStaticAnalyserState, props: RemixUiStaticAnalyserProps, isSupportedVersion, showSlither, categoryIndex: number[], groupedModules, runner, _paq, message, showWarnings, allWarnings: React.RefObject<any>, warningContainer: React.RefObject<any>, calculateWarningStateEntries: (e:[string, any][]) => {length: number, errors: any[] }, warningState, setHints: React.Dispatch<React.SetStateAction<SolHintReport[]>>, hints: SolHintReport[], setSlitherWarnings: React.Dispatch<React.SetStateAction<any[]>>, setSsaWarnings: React.Dispatch<React.SetStateAction<any[]>>,
slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<boolean>>) { slitherEnabled: boolean, setStartAnalysis: React.Dispatch<React.SetStateAction<boolean>>, solhintEnabled: boolean, basicEnabled: boolean) {
setStartAnalysis(true) setStartAnalysis(true)
setHints([]) setHints([])
setSsaWarnings([]) setSsaWarnings([])
setSlitherWarnings([]) setSlitherWarnings([])
if (!isSupportedVersion) return if (!isSupportedVersion) return
if (state.data !== null) { if (state.data !== null) {
if (lastCompilationResult && (categoryIndex.length > 0 || showSlither)) { if (lastCompilationResult && (solhintEnabled || basicEnabled || showSlither)) {
const warningMessage = [] const warningMessage = []
const warningErrors = [] const warningErrors = []
props.analysisModule.hints = []
// Run solhint // Run solhint
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'solHint'])
const hintsResult = await props.analysisModule.call('solhint', 'lint', state.file) const hintsResult = await props.analysisModule.call('solhint', 'lint', state.file)
props.analysisModule.hints = solhintEnabled === false ? 0 : hintsResult
setHints(hintsResult) setHints(hintsResult)
const warningResult = calculateWarningStateEntries(Object.entries(warningState))
props.analysisModule.emit('statusChanged', { key: hints.length+warningResult.length,
title: `${hints.length+warningResult.length} warning${hints.length+warningResult.length === 1 ? '' : 's'}`, type: 'warning'})
// Remix Analysis // Remix Analysis
_paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer']) _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer'])
const results = runner.run(lastCompilationResult, categoryIndex) const results = runner.run(lastCompilationResult, categoryIndex)

@ -1,9 +1,11 @@
import React from 'react' import React, { useEffect, useState } from 'react'
import { ErrorRendererOptions } from '../../staticanalyser' import { ErrorRendererOptions } from '../../staticanalyser'
type BasicTitleProps = { type BasicTitleProps = {
warningStateEntries: any warningStateEntries: any
ssaWarnings: any[]
hideWarnings?: boolean hideWarnings?: boolean
showLibsWarnings?: boolean
} }
type warningResultOption = { type warningResultOption = {
@ -30,15 +32,22 @@ export function calculateWarningStateEntries(entries: [string, any][]) {
} }
export function BasicTitle(props: BasicTitleProps) { export function BasicTitle(props: BasicTitleProps) {
const [filteredTotal, setFilteredTotal] = useState(0)
useEffect(() => {
setFilteredTotal(props.ssaWarnings.filter(x => !x.options.isLibrary && x.hasWarning).length)
}, [props, props.ssaWarnings.filter(x => !x.options.isLibrary && x.hasWarning).length])
return ( return (
<span>Remix{props.warningStateEntries.length > 0 ? !props.hideWarnings ? <i data-id="StaticAnalysisErrorCount" className={`badge ${calculateWarningStateEntries(props.warningStateEntries).length > 0 ? 'badge-warning' : 'badge-danger'} rounded-circle ml-1 text-center`}>{calculateWarningStateEntries(props.warningStateEntries).length}</i>: ( <span id="ssaRemixtab">Remix{props.ssaWarnings.length > 0 ? !props.hideWarnings ? !props.showLibsWarnings ? <span data-id="RemixStaticAnalysisErrorCount" className={`badge ${props.ssaWarnings.filter(x => x.hasWarning).length > 0 ? 'badge-warning' : props.ssaWarnings.filter(x => x.options.type === 'errors').length > 0 ? 'badge-danger' : 'badge-info'} badge-pill mx-1 ml-1 text-center`}>
<i className="badge badge-warning rounded-circle ml-1 text-center"> {filteredTotal}
</span>: (
<span data-id="RemixStaticAnalysisErrorCount" className={`badge ${props.ssaWarnings.filter(x => x.options.type !== 'warning' && x.options.type !== 'error').length > 0 ? 'badge-info' : props.ssaWarnings.filter(x => x.options.type === 'errors').length > 0 ? 'badge-danger' : 'badge-warning'} badge-pill mx-1 ml-1 text-center`}>
{ {
calculateWarningStateEntries(props.warningStateEntries).errors.length props.ssaWarnings.length
} }
</i> </span>
) : null} ) : null : null}
</span> </span>
) )
} }

@ -1,3 +1,4 @@
import { CompilationResult, SourceWithTarget } from "@remixproject/plugin-api"
import { RemixUiStaticAnalyserReducerActionType, RemixUiStaticAnalyserState } from "../../staticanalyser" import { RemixUiStaticAnalyserReducerActionType, RemixUiStaticAnalyserState } from "../../staticanalyser"
export const initialState: RemixUiStaticAnalyserState = { export const initialState: RemixUiStaticAnalyserState = {
@ -26,3 +27,12 @@ export const analysisReducer = (state: RemixUiStaticAnalyserState,
return initialState return initialState
} }
} }
type someReducerState = {
solhintEnabled?: boolean
basicEnabled?: boolean
slitherEnabled?: boolean
isSupportedVersion?: boolean
compiledState?: { data: CompilationResult, langVersion: string, fileName: string, source: SourceWithTarget, input: string }
}

@ -4,21 +4,29 @@ import { ViewPlugin } from '@remixproject/engine-web';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import Registry from '../state/registry'; import Registry from '../state/registry';
export declare class AnalysisTab extends ViewPlugin { export declare class AnalysisTab extends ViewPlugin {
event: EventManager; event: EventManager;
events: EventEmitter; events: EventEmitter;
registry: Registry; hints: {
element: HTMLDivElement; formattedMessage: string;
_components: any; type: any;
_deps: { column: number;
offsetToLineColumnConverter: any; line: number;
}; }[]
dispatch: any; internalCount: number
constructor(); registry: Registry;
onActivation(): Promise<void>; element: HTMLDivElement;
setDispatch(dispatch: any): void; _components: any;
render(): JSX.Element; _deps: {
updateComponent(state: any): JSX.Element; offsetToLineColumnConverter: any;
renderComponent(): void; };
dispatch: any;
constructor();
onActivation(): Promise<void>;
changedStatus(payload: any[]): Promise<void>;
setDispatch(dispatch: any): void;
render(): JSX.Element;
updateComponent(state: any): JSX.Element;
renderComponent(): void;
} }
type RemixUiStaticAnalyserState = { type RemixUiStaticAnalyserState = {
@ -51,10 +59,10 @@ type RemixUiStaticAnalyserReducerActionType = {
interface ErrorRendererProps { interface ErrorRendererProps {
message: any; message: any;
opt: ErrorRendererOptions, options: ErrorRendererOptions,
warningErrors: any hasWarning: boolean,
editor: any, editor: any,
name: string, warningModuleName: string,
} }
type ErrorRendererOptions = { type ErrorRendererOptions = {
@ -135,4 +143,4 @@ export type SlitherAnalysisResults = {
count: number, count: number,
data: SlitherAnalysisResultType[] data: SlitherAnalysisResultType[]
status: boolean status: boolean
} }

@ -48,18 +48,32 @@ function Badge ({ badgeStatus }: BadgeProps) {
return ( return (
<> <>
{ {badgeStatus && checkStatusKeyValue(badgeStatus.key, badgeStatus.type) ? (
badgeStatus && checkStatusKeyValue(badgeStatus.key, badgeStatus.type) ? ( <CustomTooltip
<i placement={"right"}
className={`${resolveClasses(badgeStatus.key, badgeStatus.type!)}`} tooltipClasses="text-nowrap"
aria-hidden="true" tooltipId="verticalItemsbadge"
> tooltipText={badgeStatus.title}
{ badgeStatus.pluginName && badgeStatus.pluginName === 'solidityStaticAnalysis' ? badgeStatus.type === 'warning' || badgeStatus.type === 'error' ? <span> >
<i className="far fa-exclamation-triangle"></i></span> <i
: <span>&nbsp;</span> : badgeStatus.text } className={`${resolveClasses(badgeStatus.key, badgeStatus.type!)}`}
</i> aria-hidden="true"
) : null >
} {badgeStatus.pluginName &&
badgeStatus.pluginName === "solidityStaticAnalysis" ? (
badgeStatus.type === "warning" || badgeStatus.type === "error" || badgeStatus.type === "danger" ? (
<span>
<i className="far fa-exclamation-triangle"></i>
</span>
) : (
<span>&nbsp;</span>
)
) : (
badgeStatus.text
)}
</i>
</CustomTooltip>
) : null}
</> </>
) )
} }

Loading…
Cancel
Save