Merge branch 'main' into robertbrignull/automodel-sort-order
This commit is contained in:
@@ -590,6 +590,14 @@ interface StopGeneratingMethodsFromLlmMessage {
|
||||
packageName: string;
|
||||
}
|
||||
|
||||
interface StartModelEvaluationMessage {
|
||||
t: "startModelEvaluation";
|
||||
}
|
||||
|
||||
interface StopModelEvaluationMessage {
|
||||
t: "stopModelEvaluation";
|
||||
}
|
||||
|
||||
interface ModelDependencyMessage {
|
||||
t: "modelDependency";
|
||||
}
|
||||
@@ -648,7 +656,9 @@ export type FromModelEditorMessage =
|
||||
| StopGeneratingMethodsFromLlmMessage
|
||||
| ModelDependencyMessage
|
||||
| HideModeledMethodsMessage
|
||||
| SetMultipleModeledMethodsMessage;
|
||||
| SetMultipleModeledMethodsMessage
|
||||
| StartModelEvaluationMessage
|
||||
| StopModelEvaluationMessage;
|
||||
|
||||
interface RevealInEditorMessage {
|
||||
t: "revealInModelEditor";
|
||||
|
||||
@@ -1,4 +1,29 @@
|
||||
import type { Result } from "sarif";
|
||||
import type { Location, Result } from "sarif";
|
||||
|
||||
function toCanonicalLocation(location: Location): Location {
|
||||
if (location.physicalLocation?.artifactLocation?.index !== undefined) {
|
||||
const canonicalLocation = {
|
||||
...location,
|
||||
};
|
||||
|
||||
canonicalLocation.physicalLocation = {
|
||||
...canonicalLocation.physicalLocation,
|
||||
};
|
||||
|
||||
canonicalLocation.physicalLocation.artifactLocation = {
|
||||
...canonicalLocation.physicalLocation.artifactLocation,
|
||||
};
|
||||
|
||||
// The index is dependent on the result of the SARIF file and usually doesn't really tell
|
||||
// us anything useful, so we remove it from the comparison.
|
||||
delete canonicalLocation.physicalLocation.artifactLocation.index;
|
||||
|
||||
return canonicalLocation;
|
||||
}
|
||||
|
||||
// Don't create a new object if we don't need to
|
||||
return location;
|
||||
}
|
||||
|
||||
function toCanonicalResult(result: Result): Result {
|
||||
const canonicalResult = {
|
||||
@@ -6,29 +31,45 @@ function toCanonicalResult(result: Result): Result {
|
||||
};
|
||||
|
||||
if (canonicalResult.locations) {
|
||||
canonicalResult.locations = canonicalResult.locations.map((location) => {
|
||||
if (location.physicalLocation?.artifactLocation?.index !== undefined) {
|
||||
const canonicalLocation = {
|
||||
...location,
|
||||
canonicalResult.locations =
|
||||
canonicalResult.locations.map(toCanonicalLocation);
|
||||
}
|
||||
|
||||
if (canonicalResult.relatedLocations) {
|
||||
canonicalResult.relatedLocations =
|
||||
canonicalResult.relatedLocations.map(toCanonicalLocation);
|
||||
}
|
||||
|
||||
if (canonicalResult.codeFlows) {
|
||||
canonicalResult.codeFlows = canonicalResult.codeFlows.map((codeFlow) => {
|
||||
if (codeFlow.threadFlows) {
|
||||
return {
|
||||
...codeFlow,
|
||||
threadFlows: codeFlow.threadFlows.map((threadFlow) => {
|
||||
if (threadFlow.locations) {
|
||||
return {
|
||||
...threadFlow,
|
||||
locations: threadFlow.locations.map((threadFlowLocation) => {
|
||||
if (threadFlowLocation.location) {
|
||||
return {
|
||||
...threadFlowLocation,
|
||||
location: toCanonicalLocation(
|
||||
threadFlowLocation.location,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return threadFlowLocation;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return threadFlow;
|
||||
}),
|
||||
};
|
||||
|
||||
canonicalLocation.physicalLocation = {
|
||||
...canonicalLocation.physicalLocation,
|
||||
};
|
||||
|
||||
canonicalLocation.physicalLocation.artifactLocation = {
|
||||
...canonicalLocation.physicalLocation.artifactLocation,
|
||||
};
|
||||
|
||||
// The index is dependent on the result of the SARIF file and usually doesn't really tell
|
||||
// us anything useful, so we remove it from the comparison.
|
||||
delete canonicalLocation.physicalLocation.artifactLocation.index;
|
||||
|
||||
return canonicalLocation;
|
||||
}
|
||||
|
||||
// Don't create a new object if we don't need to
|
||||
return location;
|
||||
return codeFlow;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -715,6 +715,7 @@ const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
||||
"llmGenerationDevEndpoint",
|
||||
MODEL_SETTING,
|
||||
);
|
||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||
const EXTENSIONS_DIRECTORY = new Setting("extensionsDirectory", MODEL_SETTING);
|
||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
||||
const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting(
|
||||
@@ -759,6 +760,10 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
return LLM_GENERATION_DEV_ENDPOINT.getValue<string | undefined>();
|
||||
}
|
||||
|
||||
public get modelEvaluation(): boolean {
|
||||
return !!MODEL_EVALUATION.getValue<boolean>();
|
||||
}
|
||||
|
||||
public getExtensionsDirectory(languageId: string): string | undefined {
|
||||
return EXTENSIONS_DIRECTORY.getValue<string>({
|
||||
languageId,
|
||||
|
||||
@@ -337,6 +337,12 @@ export class ModelEditorView extends AbstractWebview<
|
||||
this.setModeledMethods(msg.methodSignature, msg.modeledMethods);
|
||||
break;
|
||||
}
|
||||
case "startModelEvaluation":
|
||||
this.startModelEvaluation();
|
||||
break;
|
||||
case "stopModelEvaluation":
|
||||
this.stopModelEvaluation();
|
||||
break;
|
||||
case "telemetry":
|
||||
telemetryListener?.sendUIInteraction(msg.action);
|
||||
break;
|
||||
@@ -402,6 +408,8 @@ export class ModelEditorView extends AbstractWebview<
|
||||
const showLlmButton =
|
||||
this.databaseItem.language === "java" && this.modelConfig.llmGeneration;
|
||||
|
||||
const showEvaluationUi = this.modelConfig.modelEvaluation;
|
||||
|
||||
const sourceArchiveAvailable =
|
||||
this.databaseItem.hasSourceArchiveInExplorer();
|
||||
|
||||
@@ -416,6 +424,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
language: this.language,
|
||||
showGenerateButton,
|
||||
showLlmButton,
|
||||
showEvaluationUi,
|
||||
mode: this.modelingStore.getMode(this.databaseItem),
|
||||
showModeSwitchButton,
|
||||
sourceArchiveAvailable,
|
||||
@@ -910,4 +919,12 @@ export class ModelEditorView extends AbstractWebview<
|
||||
);
|
||||
this.modelingStore.addModifiedMethod(this.databaseItem, signature);
|
||||
}
|
||||
|
||||
private startModelEvaluation() {
|
||||
// Do nothing for now. This will be fleshed out in the near future.
|
||||
}
|
||||
|
||||
private stopModelEvaluation() {
|
||||
// Do nothing for now. This will be fleshed out in the near future.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,19 +111,27 @@ export function modeledMethodSupportsProvenance(
|
||||
);
|
||||
}
|
||||
|
||||
export function isModelAccepted(
|
||||
export function isModelPending(
|
||||
modeledMethod: ModeledMethod | undefined,
|
||||
modelingStatus: ModelingStatus,
|
||||
processedByAutoModel?: boolean,
|
||||
): boolean {
|
||||
if (!modeledMethod) {
|
||||
if (
|
||||
(!modeledMethod || modeledMethod.type === "none") &&
|
||||
processedByAutoModel
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!modeledMethod) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
modelingStatus !== "unsaved" ||
|
||||
modeledMethod.type === "none" ||
|
||||
!modeledMethodSupportsProvenance(modeledMethod) ||
|
||||
modeledMethod.provenance !== "ai-generated"
|
||||
modelingStatus === "unsaved" &&
|
||||
modeledMethod.type !== "none" &&
|
||||
modeledMethodSupportsProvenance(modeledMethod) &&
|
||||
modeledMethod.provenance === "ai-generated"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface ModelEditorViewState {
|
||||
language: QueryLanguage;
|
||||
showGenerateButton: boolean;
|
||||
showLlmButton: boolean;
|
||||
showEvaluationUi: boolean;
|
||||
mode: Mode;
|
||||
showModeSwitchButton: boolean;
|
||||
sourceArchiveAvailable: boolean;
|
||||
|
||||
@@ -66,5 +66,5 @@ export const ModelingNotAccepted = Template.bind({});
|
||||
ModelingNotAccepted.args = {
|
||||
method,
|
||||
modeledMethod: generatedModeledMethod,
|
||||
modelingStatus: "unsaved",
|
||||
modelPending: true,
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ModelOutputDropdown } from "../model-editor/ModelOutputDropdown";
|
||||
import { ModelKindDropdown } from "../model-editor/ModelKindDropdown";
|
||||
import { InProgressDropdown } from "../model-editor/InProgressDropdown";
|
||||
import type { QueryLanguage } from "../../common/query-language";
|
||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
|
||||
const Container = styled.div`
|
||||
padding-top: 0.5rem;
|
||||
@@ -27,7 +26,7 @@ export type MethodModelingInputsProps = {
|
||||
language: QueryLanguage;
|
||||
method: Method;
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
modelingStatus: ModelingStatus;
|
||||
modelPending: boolean;
|
||||
isModelingInProgress: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
@@ -36,7 +35,7 @@ export const MethodModelingInputs = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
isModelingInProgress,
|
||||
onChange,
|
||||
}: MethodModelingInputsProps): React.JSX.Element => {
|
||||
@@ -44,7 +43,7 @@ export const MethodModelingInputs = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
onChange,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { Method } from "../../model-editor/method";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import { isModelPending } from "../../model-editor/modeled-method";
|
||||
import {
|
||||
canAddNewModeledMethod,
|
||||
canRemoveModeledMethod,
|
||||
@@ -156,7 +157,10 @@ export const MultipleModeledMethodsPanel = ({
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethods[selectedIndex]}
|
||||
modelingStatus={modelingStatus}
|
||||
modelPending={isModelPending(
|
||||
modeledMethods[selectedIndex],
|
||||
modelingStatus,
|
||||
)}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
@@ -165,7 +169,7 @@ export const MultipleModeledMethodsPanel = ({
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethod={undefined}
|
||||
modelingStatus={modelingStatus}
|
||||
modelPending={false}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
const language = QueryLanguage.Java;
|
||||
const method = createMethod();
|
||||
const modeledMethod = createSinkModeledMethod();
|
||||
const modelingStatus = "unmodeled";
|
||||
const modelPending = false;
|
||||
const isModelingInProgress = false;
|
||||
const onChange = jest.fn();
|
||||
|
||||
@@ -26,7 +26,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
isModelingInProgress,
|
||||
onChange,
|
||||
});
|
||||
@@ -53,7 +53,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
isModelingInProgress,
|
||||
onChange,
|
||||
});
|
||||
@@ -76,7 +76,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
isModelingInProgress,
|
||||
onChange,
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethod={updatedModeledMethod}
|
||||
modelingStatus={modelingStatus}
|
||||
modelPending={modelPending}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
@@ -121,7 +121,7 @@ describe(MethodModelingInputs.name, () => {
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
isModelingInProgress: true,
|
||||
onChange,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { styled } from "styled-components";
|
||||
import { Dropdown } from "../common/Dropdown";
|
||||
|
||||
export const InputDropdown = styled(Dropdown)<{ $accepted: boolean }>`
|
||||
font-style: ${(props) => (props.$accepted ? "normal" : "italic")};
|
||||
export const InputDropdown = styled(Dropdown)<{ $pending: boolean }>`
|
||||
font-style: ${(props) => (props.$pending ? "italic" : "normal")};
|
||||
`;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { vscode } from "../vscode-api";
|
||||
|
||||
import type { Method } from "../../model-editor/method";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import { isModelPending } from "../../model-editor/modeled-method";
|
||||
import { ModelKindDropdown } from "./ModelKindDropdown";
|
||||
import { Mode } from "../../model-editor/shared/mode";
|
||||
import { MethodClassifications } from "./MethodClassifications";
|
||||
@@ -112,6 +113,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
modeledMethods: modeledMethodsProp,
|
||||
methodIsUnsaved,
|
||||
methodIsSelected,
|
||||
processedByAutoModel,
|
||||
viewState,
|
||||
revealedMethodSignature,
|
||||
inputAccessPathSuggestions,
|
||||
@@ -255,88 +257,96 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
)}
|
||||
{!props.modelingInProgress && (
|
||||
<>
|
||||
{modeledMethods.map((modeledMethod, index) => (
|
||||
<DataGridRow key={index} focused={focusedIndex === index}>
|
||||
<DataGridCell>
|
||||
<ModelTypeDropdown
|
||||
language={viewState.language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus={modelingStatus}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{inputAccessPathSuggestions === undefined ? (
|
||||
<ModelInputDropdown
|
||||
{modeledMethods.map((modeledMethod, index) => {
|
||||
const modelPending = isModelPending(
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
processedByAutoModel,
|
||||
);
|
||||
|
||||
return (
|
||||
<DataGridRow key={index} focused={focusedIndex === index}>
|
||||
<DataGridCell>
|
||||
<ModelTypeDropdown
|
||||
language={viewState.language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus={modelingStatus}
|
||||
modelPending={modelPending}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
) : (
|
||||
<ModelInputSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={inputAccessPathSuggestions}
|
||||
typePathSuggestions={outputAccessPathSuggestions ?? []}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
)}
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{outputAccessPathSuggestions === undefined ? (
|
||||
<ModelOutputDropdown
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{inputAccessPathSuggestions === undefined ? (
|
||||
<ModelInputDropdown
|
||||
language={viewState.language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
modelPending={modelPending}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
) : (
|
||||
<ModelInputSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={inputAccessPathSuggestions}
|
||||
typePathSuggestions={outputAccessPathSuggestions ?? []}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
)}
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{outputAccessPathSuggestions === undefined ? (
|
||||
<ModelOutputDropdown
|
||||
language={viewState.language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
modelPending={modelPending}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
) : (
|
||||
<ModelOutputSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={outputAccessPathSuggestions}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
)}
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
<ModelKindDropdown
|
||||
language={viewState.language}
|
||||
method={method}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus={modelingStatus}
|
||||
modelPending={modelPending}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
) : (
|
||||
<ModelOutputSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={outputAccessPathSuggestions}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
)}
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
<ModelKindDropdown
|
||||
language={viewState.language}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus={modelingStatus}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{index === 0 ? (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Add new model"
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
handleAddModelClick();
|
||||
}}
|
||||
disabled={addModelButtonDisabled}
|
||||
>
|
||||
<Codicon name="add" />
|
||||
</CodiconRow>
|
||||
) : (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Remove model"
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
removeModelClickedHandlers[index]();
|
||||
}}
|
||||
>
|
||||
<Codicon name="trash" />
|
||||
</CodiconRow>
|
||||
)}
|
||||
</DataGridCell>
|
||||
</DataGridRow>
|
||||
))}
|
||||
</DataGridCell>
|
||||
<DataGridCell>
|
||||
{index === 0 ? (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Add new model"
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
handleAddModelClick();
|
||||
}}
|
||||
disabled={addModelButtonDisabled}
|
||||
>
|
||||
<Codicon name="add" />
|
||||
</CodiconRow>
|
||||
) : (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Remove model"
|
||||
onClick={(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
removeModelClickedHandlers[index]();
|
||||
}}
|
||||
>
|
||||
<Codicon name="trash" />
|
||||
</CodiconRow>
|
||||
)}
|
||||
</DataGridCell>
|
||||
</DataGridRow>
|
||||
);
|
||||
})}
|
||||
{validationErrors.map((error, index) => (
|
||||
<DataGridCell gridColumn="span 5" key={index}>
|
||||
<ModeledMethodAlert
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ToModelEditorMessage } from "../../common/interface-types";
|
||||
import {
|
||||
VSCodeButton,
|
||||
VSCodeCheckbox,
|
||||
VSCodeProgressRing,
|
||||
VSCodeTag,
|
||||
} from "@vscode/webview-ui-toolkit/react";
|
||||
import { styled } from "styled-components";
|
||||
@@ -74,6 +75,57 @@ const ButtonsContainer = styled.div`
|
||||
margin-top: 1rem;
|
||||
`;
|
||||
|
||||
const ProgressRing = styled(VSCodeProgressRing)`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
const ModelEvaluation = ({
|
||||
viewState,
|
||||
modeledMethods,
|
||||
modifiedSignatures,
|
||||
onStartEvaluation,
|
||||
onStopEvaluation,
|
||||
evaluationInProgress,
|
||||
}: {
|
||||
viewState: ModelEditorViewState;
|
||||
modeledMethods: Record<string, ModeledMethod[]>;
|
||||
modifiedSignatures: Set<string>;
|
||||
onStartEvaluation: () => void;
|
||||
onStopEvaluation: () => void;
|
||||
evaluationInProgress: boolean;
|
||||
}) => {
|
||||
if (!viewState.showEvaluationUi) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluationInProgress) {
|
||||
const customModelsExist = Object.values(modeledMethods).some(
|
||||
(methods) => methods.filter((m) => m.type !== "none").length > 0,
|
||||
);
|
||||
|
||||
const unsavedChanges = modifiedSignatures.size > 0;
|
||||
|
||||
return (
|
||||
<VSCodeButton
|
||||
onClick={onStartEvaluation}
|
||||
appearance="secondary"
|
||||
disabled={!customModelsExist || unsavedChanges}
|
||||
>
|
||||
Evaluate
|
||||
</VSCodeButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VSCodeButton onClick={onStopEvaluation} appearance="secondary">
|
||||
<ProgressRing />
|
||||
Stop evaluation
|
||||
</VSCodeButton>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
initialViewState?: ModelEditorViewState;
|
||||
initialMethods?: Method[];
|
||||
@@ -114,6 +166,8 @@ export function ModelEditor({
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
vscode.postMessage({
|
||||
t: "hideModeledMethods",
|
||||
@@ -254,6 +308,20 @@ export function ModelEditor({
|
||||
[selectedSignatures],
|
||||
);
|
||||
|
||||
const onStartEvaluation = useCallback(() => {
|
||||
setEvaluationInProgress(true);
|
||||
vscode.postMessage({
|
||||
t: "startModelEvaluation",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onStopEvaluation = useCallback(() => {
|
||||
setEvaluationInProgress(false);
|
||||
vscode.postMessage({
|
||||
t: "stopModelEvaluation",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onGenerateFromSourceClick = useCallback(() => {
|
||||
vscode.postMessage({
|
||||
t: "generateMethod",
|
||||
@@ -373,6 +441,14 @@ export function ModelEditor({
|
||||
Generate
|
||||
</VSCodeButton>
|
||||
)}
|
||||
<ModelEvaluation
|
||||
viewState={viewState}
|
||||
modeledMethods={modeledMethods}
|
||||
modifiedSignatures={modifiedSignatures}
|
||||
onStartEvaluation={onStartEvaluation}
|
||||
onStopEvaluation={onStopEvaluation}
|
||||
evaluationInProgress={evaluationInProgress}
|
||||
/>
|
||||
</ButtonsContainer>
|
||||
</HeaderRow>
|
||||
</HeaderColumn>
|
||||
|
||||
@@ -3,13 +3,11 @@ import { useCallback, useMemo } from "react";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import {
|
||||
calculateNewProvenance,
|
||||
isModelAccepted,
|
||||
modeledMethodSupportsInput,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import type { Method } from "../../model-editor/method";
|
||||
import type { QueryLanguage } from "../../common/query-language";
|
||||
import { getModelsAsDataLanguage } from "../../model-editor/languages";
|
||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
import { InputDropdown } from "./InputDropdown";
|
||||
import { ModelTypeTextbox } from "./ModelTypeTextbox";
|
||||
|
||||
@@ -17,7 +15,7 @@ type Props = {
|
||||
language: QueryLanguage;
|
||||
method: Method;
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
modelingStatus: ModelingStatus;
|
||||
modelPending: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
@@ -25,7 +23,7 @@ export const ModelInputDropdown = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
onChange,
|
||||
}: Props): React.JSX.Element => {
|
||||
const options = useMemo(() => {
|
||||
@@ -77,14 +75,12 @@ export const ModelInputDropdown = ({
|
||||
);
|
||||
}
|
||||
|
||||
const modelAccepted = isModelAccepted(modeledMethod, modelingStatus);
|
||||
|
||||
return (
|
||||
<InputDropdown
|
||||
value={value}
|
||||
options={options}
|
||||
disabled={!enabled}
|
||||
$accepted={modelAccepted}
|
||||
$pending={modelPending}
|
||||
onChange={handleChange}
|
||||
aria-label="Input"
|
||||
/>
|
||||
|
||||
@@ -6,25 +6,23 @@ import type {
|
||||
} from "../../model-editor/modeled-method";
|
||||
import {
|
||||
modeledMethodSupportsKind,
|
||||
isModelAccepted,
|
||||
calculateNewProvenance,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import { getModelsAsDataLanguage } from "../../model-editor/languages";
|
||||
import type { QueryLanguage } from "../../common/query-language";
|
||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
import { InputDropdown } from "./InputDropdown";
|
||||
|
||||
type Props = {
|
||||
language: QueryLanguage;
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
modelingStatus: ModelingStatus;
|
||||
modelPending: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
export const ModelKindDropdown = ({
|
||||
language,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
onChange,
|
||||
}: Props) => {
|
||||
const predicate = useMemo(() => {
|
||||
@@ -89,14 +87,12 @@ export const ModelKindDropdown = ({
|
||||
}
|
||||
}, [modeledMethod, value, kinds, onChangeKind]);
|
||||
|
||||
const modelAccepted = isModelAccepted(modeledMethod, modelingStatus);
|
||||
|
||||
return (
|
||||
<InputDropdown
|
||||
value={value}
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
$accepted={modelAccepted}
|
||||
$pending={modelPending}
|
||||
onChange={handleChange}
|
||||
aria-label="Kind"
|
||||
/>
|
||||
|
||||
@@ -3,13 +3,11 @@ import { useCallback, useMemo } from "react";
|
||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import {
|
||||
calculateNewProvenance,
|
||||
isModelAccepted,
|
||||
modeledMethodSupportsOutput,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import type { Method } from "../../model-editor/method";
|
||||
import { getModelsAsDataLanguage } from "../../model-editor/languages";
|
||||
import type { QueryLanguage } from "../../common/query-language";
|
||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
import { InputDropdown } from "./InputDropdown";
|
||||
import { ModelTypeTextbox } from "./ModelTypeTextbox";
|
||||
|
||||
@@ -17,7 +15,7 @@ type Props = {
|
||||
language: QueryLanguage;
|
||||
method: Method;
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
modelingStatus: ModelingStatus;
|
||||
modelPending: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
@@ -25,7 +23,7 @@ export const ModelOutputDropdown = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
onChange,
|
||||
}: Props): React.JSX.Element => {
|
||||
const options = useMemo(() => {
|
||||
@@ -78,14 +76,12 @@ export const ModelOutputDropdown = ({
|
||||
);
|
||||
}
|
||||
|
||||
const modelAccepted = isModelAccepted(modeledMethod, modelingStatus);
|
||||
|
||||
return (
|
||||
<InputDropdown
|
||||
value={value}
|
||||
options={options}
|
||||
disabled={!enabled}
|
||||
$accepted={modelAccepted}
|
||||
$pending={modelPending}
|
||||
onChange={handleChange}
|
||||
aria-label="Output"
|
||||
/>
|
||||
|
||||
@@ -4,10 +4,7 @@ import type {
|
||||
ModeledMethod,
|
||||
ModeledMethodType,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import {
|
||||
calculateNewProvenance,
|
||||
isModelAccepted,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import { calculateNewProvenance } from "../../model-editor/modeled-method";
|
||||
import type { Method } from "../../model-editor/method";
|
||||
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
|
||||
import type { Mutable } from "../../common/mutable";
|
||||
@@ -15,14 +12,13 @@ import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
|
||||
import type { QueryLanguage } from "../../common/query-language";
|
||||
import type { ModelsAsDataLanguagePredicates } from "../../model-editor/languages";
|
||||
import { getModelsAsDataLanguage } from "../../model-editor/languages";
|
||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
import { InputDropdown } from "./InputDropdown";
|
||||
|
||||
type Props = {
|
||||
language: QueryLanguage;
|
||||
method: Method;
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
modelingStatus: ModelingStatus;
|
||||
modelPending: boolean;
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
@@ -40,7 +36,7 @@ export const ModelTypeDropdown = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethod,
|
||||
modelingStatus,
|
||||
modelPending,
|
||||
onChange,
|
||||
}: Props): React.JSX.Element => {
|
||||
const options = useMemo(() => {
|
||||
@@ -114,13 +110,11 @@ export const ModelTypeDropdown = ({
|
||||
);
|
||||
}
|
||||
|
||||
const modelAccepted = isModelAccepted(modeledMethod, modelingStatus);
|
||||
|
||||
return (
|
||||
<InputDropdown
|
||||
value={modeledMethod?.type ?? "none"}
|
||||
options={options}
|
||||
$accepted={modelAccepted}
|
||||
$pending={modelPending}
|
||||
onChange={handleChange}
|
||||
aria-label="Model type"
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(ModelKindDropdown.name, () => {
|
||||
<ModelKindDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
@@ -47,7 +47,7 @@ describe(ModelKindDropdown.name, () => {
|
||||
<ModelKindDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
@@ -64,7 +64,7 @@ describe(ModelKindDropdown.name, () => {
|
||||
<ModelKindDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={updatedModeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
@@ -82,7 +82,7 @@ describe(ModelKindDropdown.name, () => {
|
||||
<ModelKindDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
@@ -102,7 +102,7 @@ describe(ModelKindDropdown.name, () => {
|
||||
<ModelKindDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ describe(ModelTypeDropdown.name, () => {
|
||||
<ModelTypeDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
method={method}
|
||||
/>,
|
||||
@@ -42,7 +42,7 @@ describe(ModelTypeDropdown.name, () => {
|
||||
<ModelTypeDropdown
|
||||
language={QueryLanguage.Ruby}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
method={method}
|
||||
/>,
|
||||
@@ -64,7 +64,7 @@ describe(ModelTypeDropdown.name, () => {
|
||||
<ModelTypeDropdown
|
||||
language={QueryLanguage.Java}
|
||||
modeledMethod={modeledMethod}
|
||||
modelingStatus="unsaved"
|
||||
modelPending={false}
|
||||
onChange={onChange}
|
||||
method={method}
|
||||
/>,
|
||||
|
||||
@@ -11,6 +11,7 @@ export function createMockModelEditorViewState(
|
||||
mode: Mode.Application,
|
||||
showGenerateButton: false,
|
||||
showLlmButton: false,
|
||||
showEvaluationUi: false,
|
||||
showModeSwitchButton: true,
|
||||
extensionPack: createMockExtensionPack(),
|
||||
sourceArchiveAvailable: true,
|
||||
|
||||
@@ -138,6 +138,364 @@ describe("sarifDiff", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not take into account the location index when in thread flows or related locations", () => {
|
||||
const result1: Result = {
|
||||
ruleId: "java/static-initialization-vector",
|
||||
ruleIndex: 0,
|
||||
rule: {
|
||||
id: "java/static-initialization-vector",
|
||||
index: 0,
|
||||
},
|
||||
message: {
|
||||
text: "A [static initialization vector](1) should not be used for encryption.",
|
||||
},
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 126,
|
||||
},
|
||||
region: {
|
||||
startLine: 1272,
|
||||
startColumn: 55,
|
||||
endColumn: 61,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "9a2a0c085da38206:3",
|
||||
primaryLocationStartColumnFingerprint: "38",
|
||||
},
|
||||
codeFlows: [
|
||||
{
|
||||
threadFlows: [
|
||||
{
|
||||
locations: [
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 126,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 50,
|
||||
endColumn: 76,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "new byte[] : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 52,
|
||||
startColumn: 28,
|
||||
endColumn: 37,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "iv : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 53,
|
||||
startColumn: 14,
|
||||
endColumn: 16,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "iv : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 53,
|
||||
startColumn: 9,
|
||||
endColumn: 32,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "this <constr(this)> [post update] : IvParameterSpec",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 126,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 30,
|
||||
endColumn: 77,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "new IvParameterSpec(...) : IvParameterSpec",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 126,
|
||||
},
|
||||
region: {
|
||||
startLine: 1272,
|
||||
startColumn: 55,
|
||||
endColumn: 61,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "params",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relatedLocations: [
|
||||
{
|
||||
id: 1,
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 126,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 50,
|
||||
endColumn: 76,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "static initialization vector",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const result2: Result = {
|
||||
ruleId: "java/static-initialization-vector",
|
||||
ruleIndex: 0,
|
||||
rule: {
|
||||
id: "java/static-initialization-vector",
|
||||
index: 0,
|
||||
},
|
||||
message: {
|
||||
text: "A [static initialization vector](1) should not be used for encryption.",
|
||||
},
|
||||
locations: [
|
||||
{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 141,
|
||||
},
|
||||
region: {
|
||||
startLine: 1272,
|
||||
startColumn: 55,
|
||||
endColumn: 61,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash: "9a2a0c085da38206:3",
|
||||
primaryLocationStartColumnFingerprint: "38",
|
||||
},
|
||||
codeFlows: [
|
||||
{
|
||||
threadFlows: [
|
||||
{
|
||||
locations: [
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 141,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 50,
|
||||
endColumn: 76,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "new byte[] : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 52,
|
||||
startColumn: 28,
|
||||
endColumn: 37,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "iv : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 53,
|
||||
startColumn: 14,
|
||||
endColumn: 16,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "iv : byte[]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 12,
|
||||
},
|
||||
region: {
|
||||
startLine: 53,
|
||||
startColumn: 9,
|
||||
endColumn: 32,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "this <constr(this)> [post update] : IvParameterSpec",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 141,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 30,
|
||||
endColumn: 77,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "new IvParameterSpec(...) : IvParameterSpec",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
location: {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 141,
|
||||
},
|
||||
region: {
|
||||
startLine: 1272,
|
||||
startColumn: 55,
|
||||
endColumn: 61,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "params",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relatedLocations: [
|
||||
{
|
||||
id: 1,
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "src/java.base/share/classes/sun/security/ssl/SSLCipher.java",
|
||||
uriBaseId: "%SRCROOT%",
|
||||
index: 141,
|
||||
},
|
||||
region: {
|
||||
startLine: 1270,
|
||||
startColumn: 50,
|
||||
endColumn: 76,
|
||||
},
|
||||
},
|
||||
message: {
|
||||
text: "static initialization vector",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(sarifDiff([result1], [result2])).toEqual({
|
||||
from: [],
|
||||
to: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not modify the input", () => {
|
||||
const result1: Result = {
|
||||
message: {
|
||||
|
||||
Reference in New Issue
Block a user