From 97b9c43ae1ce1720a59b178fc9c03551e6e3f740 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 18 Jul 2024 16:14:24 -0400 Subject: [PATCH] Display path provenance information in results view --- .../ql-vscode/src/common/interface-types.ts | 21 ++- .../ql-vscode/src/common/sarif-parser.ts | 24 +-- .../ql-vscode/src/compare/compare-view.ts | 8 + .../databases/local-databases/locations.ts | 35 ++-- .../src/local-queries/results-view.ts | 8 + .../ql-vscode/src/view/compare/Compare.tsx | 8 + .../src/view/compare/CompareTable.tsx | 10 +- .../compare/InterpretedCompareResultTable.tsx | 9 +- .../ql-vscode/src/view/results/AlertTable.tsx | 10 +- .../view/results/AlertTablePathNodeRow.tsx | 175 +++++++++++++++++- .../src/view/results/AlertTablePathRow.tsx | 5 +- .../src/view/results/AlertTableResultRow.tsx | 5 +- .../src/view/results/ResultTable.tsx | 4 +- .../src/view/results/ResultTables.tsx | 4 + .../ql-vscode/src/view/results/ResultsApp.tsx | 10 + .../results/locations/ClickableLocation.tsx | 2 +- .../src/view/results/locations/Location.tsx | 6 +- .../view/results/locations/SarifLocation.tsx | 2 +- .../src/view/results/result-table-utils.ts | 4 +- .../src/view/results/resultsView.css | 9 + 20 files changed, 310 insertions(+), 49 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index fe1003dec..1bb87b08b 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -147,6 +147,17 @@ interface SetStateMsg { parsedResultSets: ParsedResultSets; } +export interface UserSettings { + /** Whether to display links to the dataflow models that generated particular nodes in a flow path. */ + shouldShowProvenance: boolean; +} + +/** Message indicating that the user's configuration settings have changed. */ +interface SetUserSettingsMsg { + t: "setUserSettings"; + userSettings: UserSettings; +} + /** * Message indicating that the results view should display interpreted * results. @@ -191,6 +202,7 @@ interface UntoggleShowProblemsMsg { export type IntoResultsViewMsg = | ResultsUpdatingMsg | SetStateMsg + | SetUserSettingsMsg | ShowInterpretedPageMsg | NavigateMsg | UntoggleShowProblemsMsg; @@ -208,13 +220,15 @@ export type FromResultsViewMsg = | OpenFileMsg; /** - * Message from the results view to open a database source + * Message from the results view to open a source * file at the provided location. */ interface ViewSourceFileMsg { t: "viewSourceFile"; loc: UrlValueResolvable; - databaseUri: string; + /** URI of the database whose source archive contains the file, or `undefined` to open a file from + * the local disk. The latter case is used for opening links to data extension model files. */ + databaseUri: string | undefined; } /** @@ -341,7 +355,8 @@ interface ChangeCompareMessage { export type ToCompareViewMessage = | SetComparisonQueryInfoMessage - | SetComparisonsMessage; + | SetComparisonsMessage + | SetUserSettingsMsg; /** * Message to the compare view that sets the metadata of the compared queries. diff --git a/extensions/ql-vscode/src/common/sarif-parser.ts b/extensions/ql-vscode/src/common/sarif-parser.ts index b4b3b24bb..40c07d0bd 100644 --- a/extensions/ql-vscode/src/common/sarif-parser.ts +++ b/extensions/ql-vscode/src/common/sarif-parser.ts @@ -1,18 +1,20 @@ -import type { Log, Tool } from "sarif"; +import type { Log } from "sarif"; import { createReadStream } from "fs-extra"; import { connectTo } from "stream-json/Assembler"; import { getErrorMessage } from "./helpers-pure"; -import { withParser } from "stream-json/filters/Pick"; - -const DUMMY_TOOL: Tool = { driver: { name: "" } }; +import { withParser } from "stream-json/filters/Ignore"; export async function sarifParser( interpretedResultsPath: string, ): Promise { try { - // Parse the SARIF file into token streams, filtering out only the results array. + // Parse the SARIF file into token streams, filtering out some of the larger subtrees that we + // don't need. const pipeline = createReadStream(interpretedResultsPath).pipe( - withParser({ filter: "runs.0.results" }), + withParser({ + // We don't need to run's `artifacts` property, nor the driver's `notifications` property. + filter: /^runs\.\d+\.(artifacts|tool\.driver\.notifications)/, + }), ); // Creates JavaScript objects from the token stream @@ -38,15 +40,7 @@ export async function sarifParser( }); asm.on("done", (asm) => { - const log: Log = { - version: "2.1.0", - runs: [ - { - tool: DUMMY_TOOL, - results: asm.current ?? [], - }, - ], - }; + const log: Log = asm.current; resolve(log); alreadyDone = true; diff --git a/extensions/ql-vscode/src/compare/compare-view.ts b/extensions/ql-vscode/src/compare/compare-view.ts index bd2362039..8b762e298 100644 --- a/extensions/ql-vscode/src/compare/compare-view.ts +++ b/extensions/ql-vscode/src/compare/compare-view.ts @@ -33,6 +33,7 @@ import { getResultSetNames, } from "./result-set-names"; import { compareInterpretedResults } from "./interpreted-results"; +import { isCanary } from "../config"; interface ComparePair { from: CompletedLocalQueryInfo; @@ -116,6 +117,13 @@ export class CompareView extends AbstractWebview< panel.reveal(undefined, true); await this.waitForPanelLoaded(); + await this.postMessage({ + t: "setUserSettings", + userSettings: { + shouldShowProvenance: isCanary(), + }, + }); + await this.postMessage({ t: "setComparisonQueryInfo", stats: { diff --git a/extensions/ql-vscode/src/databases/local-databases/locations.ts b/extensions/ql-vscode/src/databases/local-databases/locations.ts index f3614fc67..55961c2a1 100644 --- a/extensions/ql-vscode/src/databases/local-databases/locations.ts +++ b/extensions/ql-vscode/src/databases/local-databases/locations.ts @@ -37,11 +37,12 @@ export const shownLocationLineDecoration = /** * Resolves the specified CodeQL location to a URI into the source archive. * @param loc CodeQL location to resolve. Must have a non-empty value for `loc.file`. - * @param databaseItem Database in which to resolve the file location. + * @param databaseItem Database in which to resolve the file location, or `undefined` to resolve + * from the local file system. */ function resolveFivePartLocation( loc: UrlValueLineColumnLocation, - databaseItem: DatabaseItem, + databaseItem: DatabaseItem | undefined, ): Location { // `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and // are one-based. Adjust accordingly. @@ -52,7 +53,10 @@ function resolveFivePartLocation( Math.max(1, loc.endColumn), ); - return new Location(databaseItem.resolveSourceFile(loc.uri), range); + return new Location( + databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri), + range, + ); } /** @@ -62,22 +66,26 @@ function resolveFivePartLocation( */ function resolveWholeFileLocation( loc: UrlValueWholeFileLocation, - databaseItem: DatabaseItem, + databaseItem: DatabaseItem | undefined, ): Location { // A location corresponding to the start of the file. const range = new Range(0, 0, 0, 0); - return new Location(databaseItem.resolveSourceFile(loc.uri), range); + return new Location( + databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri), + range, + ); } /** * Try to resolve the specified CodeQL location to a URI into the source archive. If no exact location * can be resolved, returns `undefined`. * @param loc CodeQL location to resolve - * @param databaseItem Database in which to resolve the file location. + * @param databaseItem Database in which to resolve the file location, or `undefined` to resolve + * from the local file system. */ export function tryResolveLocation( loc: UrlValueResolvable | undefined, - databaseItem: DatabaseItem, + databaseItem: DatabaseItem | undefined, ): Location | undefined { if (!loc) { return; @@ -95,7 +103,7 @@ export function tryResolveLocation( export async function showResolvableLocation( loc: UrlValueResolvable, - databaseItem: DatabaseItem, + databaseItem: DatabaseItem | undefined, logger: Logger, ): Promise { try { @@ -151,13 +159,14 @@ export async function showLocation(location?: Location) { } export async function jumpToLocation( - databaseUri: string, + databaseUri: string | undefined, loc: UrlValueResolvable, databaseManager: DatabaseManager, logger: Logger, ) { - const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri)); - if (databaseItem !== undefined) { - await showResolvableLocation(loc, databaseItem, logger); - } + const databaseItem = + databaseUri !== undefined + ? databaseManager.findDatabaseItem(Uri.parse(databaseUri)) + : undefined; + await showResolvableLocation(loc, databaseItem, logger); } diff --git a/extensions/ql-vscode/src/local-queries/results-view.ts b/extensions/ql-vscode/src/local-queries/results-view.ts index 2f308d7ed..18ee93f85 100644 --- a/extensions/ql-vscode/src/local-queries/results-view.ts +++ b/extensions/ql-vscode/src/local-queries/results-view.ts @@ -537,6 +537,14 @@ export class ResultsView extends AbstractWebview< resultSetNames, }; + await this.postMessage({ + t: "setUserSettings", + userSettings: { + // Only show provenance info in canary mode for now. + shouldShowProvenance: isCanary(), + }, + }); + await this.postMessage({ t: "setState", interpretation: interpretationPage, diff --git a/extensions/ql-vscode/src/view/compare/Compare.tsx b/extensions/ql-vscode/src/view/compare/Compare.tsx index 5b2b5a51d..662b8bb3f 100644 --- a/extensions/ql-vscode/src/view/compare/Compare.tsx +++ b/extensions/ql-vscode/src/view/compare/Compare.tsx @@ -5,6 +5,7 @@ import type { ToCompareViewMessage, SetComparisonsMessage, SetComparisonQueryInfoMessage, + UserSettings, } from "../../common/interface-types"; import CompareSelector from "./CompareSelector"; import { vscode } from "../vscode-api"; @@ -31,6 +32,9 @@ export function Compare(_: Record): React.JSX.Element { const [comparison, setComparison] = useState( null, ); + const [userSettings, setUserSettings] = useState({ + shouldShowProvenance: false, + }); const message = comparison?.message || "Empty comparison"; const hasRows = @@ -48,6 +52,9 @@ export function Compare(_: Record): React.JSX.Element { case "setComparisons": setComparison(msg); break; + case "setUserSettings": + setUserSettings(msg.userSettings); + break; default: assertNever(msg); } @@ -85,6 +92,7 @@ export function Compare(_: Record): React.JSX.Element { ) : ( {message} diff --git a/extensions/ql-vscode/src/view/compare/CompareTable.tsx b/extensions/ql-vscode/src/view/compare/CompareTable.tsx index 3c401c12d..ccc805aed 100644 --- a/extensions/ql-vscode/src/view/compare/CompareTable.tsx +++ b/extensions/ql-vscode/src/view/compare/CompareTable.tsx @@ -1,6 +1,7 @@ import type { SetComparisonQueryInfoMessage, SetComparisonsMessage, + UserSettings, } from "../../common/interface-types"; import { className } from "../results/result-table-utils"; import { vscode } from "../vscode-api"; @@ -12,6 +13,7 @@ import { InterpretedCompareResultTable } from "./InterpretedCompareResultTable"; interface Props { queryInfo: SetComparisonQueryInfoMessage; comparison: SetComparisonsMessage; + userSettings: UserSettings; } const OpenButton = styled(TextButton)` @@ -29,7 +31,11 @@ const Table = styled.table` } `; -export default function CompareTable({ queryInfo, comparison }: Props) { +export default function CompareTable({ + queryInfo, + comparison, + userSettings, +}: Props) { const result = comparison.result!; async function openQuery(kind: "from" | "to") { @@ -78,6 +84,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) { {result.kind === "interpreted" && ( @@ -96,6 +103,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) { {result.kind === "interpreted" && ( diff --git a/extensions/ql-vscode/src/view/compare/InterpretedCompareResultTable.tsx b/extensions/ql-vscode/src/view/compare/InterpretedCompareResultTable.tsx index b4eba7c86..82c386f04 100644 --- a/extensions/ql-vscode/src/view/compare/InterpretedCompareResultTable.tsx +++ b/extensions/ql-vscode/src/view/compare/InterpretedCompareResultTable.tsx @@ -1,27 +1,32 @@ -import type { Result } from "sarif"; +import type { Result, Run } from "sarif"; import { AlertTable } from "../results/AlertTable"; +import type { UserSettings } from "../../common/interface-types"; type Props = { results: Result[]; databaseUri: string; sourceLocationPrefix: string; + run?: Run; + userSettings: UserSettings; }; export const InterpretedCompareResultTable = ({ results, databaseUri, sourceLocationPrefix, + userSettings, }: Props) => { return ( - + Message diff --git a/extensions/ql-vscode/src/view/results/AlertTable.tsx b/extensions/ql-vscode/src/view/results/AlertTable.tsx index 2d0b165e0..c4a076584 100644 --- a/extensions/ql-vscode/src/view/results/AlertTable.tsx +++ b/extensions/ql-vscode/src/view/results/AlertTable.tsx @@ -1,4 +1,4 @@ -import type { Location, Result } from "sarif"; +import type { Location, Result, Run } from "sarif"; import type { PathNode, Result as ResultKeysResult, @@ -7,7 +7,7 @@ import type { import { getPath, getPathNode, getResult, keyToString } from "./result-keys"; import { className, jumpToLocation } from "./result-table-utils"; import { onNavigation } from "./navigation"; -import type { NavigateMsg } from "../../common/interface-types"; +import type { NavigateMsg, UserSettings } from "../../common/interface-types"; import { NavigationDirection } from "../../common/interface-types"; import { isNoLocation, parseSarifLocation } from "../../common/sarif-utils"; import { sendTelemetry } from "../common/telemetry"; @@ -21,6 +21,8 @@ type Props = { results: Result[]; databaseUri: string; sourceLocationPrefix: string; + run?: Run; + userSettings: UserSettings; numTruncatedResults?: number; header: ReactNode; @@ -31,6 +33,8 @@ export function AlertTable({ results, databaseUri, sourceLocationPrefix, + run, + userSettings, numTruncatedResults, header, noResults, @@ -202,6 +206,8 @@ export function AlertTable({ selectedItem={selectedItem} selectedItemRef={selectedItemRef} databaseUri={databaseUri} + run={run} + userSettings={userSettings} sourceLocationPrefix={sourceLocationPrefix} updateSelectionCallback={updateSelectionCallback} toggleExpanded={toggle} diff --git a/extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx b/extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx index 0a6d8a0db..64b72d23b 100644 --- a/extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx +++ b/extensions/ql-vscode/src/view/results/AlertTablePathNodeRow.tsx @@ -1,4 +1,12 @@ -import type { ThreadFlowLocation } from "sarif"; +import type { + Location as SarifLogLocation, + ReportingDescriptorReference, + ThreadFlowLocation, + Run, + PhysicalLocation, + ArtifactLocation, + ToolComponent, +} from "sarif"; import type { PathNode, Result as ResultKeysResult, @@ -9,6 +17,151 @@ import { SarifLocation } from "./locations/SarifLocation"; import { selectableZebraStripe } from "./result-table-utils"; import { useCallback, useMemo } from "react"; import { VerticalRule } from "../common/VerticalRule"; +import type { UserSettings } from "../../common/interface-types"; + +/** The definition of a taxon for a data extension model row. */ +interface ModelTaxon { + location: SarifLogLocation; +} + +/** Resolve an `ArtifactLocation` that might contain a relative reference instead of an absolute + * URI. + */ +function resolveArtifactLocation( + location: ArtifactLocation, + baseUri: URL, +): ArtifactLocation { + if (location.uri === undefined) { + // No URI at all. Just return the original location. + return location; + } + return { + ...location, + uri: new URL(location.uri, baseUri).toString(), + }; +} + +/** Get the URI of the pack's local root directory, if available. */ +function getLocalPackUri(extension: ToolComponent): URL | undefined { + if (extension.locations === undefined) { + return undefined; + } + + const localPackLocation = extension.locations.find( + (loc) => + loc.properties !== undefined && + loc.properties.tags !== undefined && + loc.properties.tags.includes("CodeQL/LocalPackRoot"), + ); + if (localPackLocation === undefined || localPackLocation.uri === undefined) { + return undefined; + } + + return new URL(localPackLocation.uri); +} + +/** Resolve a `ReportingDescriptorReference` to the `ReportingDescriptor` for the taxon that it + * refers to. + */ +function resolveTaxonDefinition( + run: Run, + taxonRef: ReportingDescriptorReference, +): ModelTaxon | undefined { + const extensions = run.tool.extensions; + if (extensions === undefined) { + return undefined; + } + + const extensionIndex = taxonRef.toolComponent?.index; + if ( + extensionIndex === undefined || + extensionIndex < 0 || + extensionIndex >= extensions.length + ) { + return undefined; + } + + const extension = extensions[extensionIndex]; + if (extension.taxa === undefined) { + return undefined; + } + + const localPackUri = getLocalPackUri(extension); + if (localPackUri === undefined) { + return undefined; + } + + const taxonIndex = taxonRef.index; + if ( + taxonIndex === undefined || + taxonIndex < 0 || + taxonIndex >= extension.taxa.length + ) { + return undefined; + } + + const taxonDef = extension.taxa[taxonIndex]; + if (taxonDef.properties === undefined) { + return undefined; + } + + const location: PhysicalLocation = + taxonDef.properties["CodeQL/DataExtensionLocation"]; + if (location === undefined || location.artifactLocation === undefined) { + return undefined; + } + + return { + location: { + physicalLocation: { + ...location, + artifactLocation: resolveArtifactLocation( + location.artifactLocation, + localPackUri, + ), + }, + }, + }; +} + +/** Generate the React elements for each taxon. */ +function taxaLocations( + taxa: ReportingDescriptorReference[] | undefined, + run: Run | undefined, + onClick: () => void, +) { + if (taxa === undefined || taxa.length === 0 || run === undefined) { + return []; + } + + return taxa.flatMap((taxonRef, index) => { + if (taxonRef.properties === undefined) { + return []; + } + + const role = taxonRef.properties["CodeQL/DataflowRole"]; + if (typeof role !== "string") { + return []; + } + + const taxonDef = resolveTaxonDefinition(run, taxonRef); + if (taxonDef === undefined) { + return []; + } + + return ( +
+ {`(${role}) `} + +
+ ); + }); +} interface Props { step: ThreadFlowLocation; @@ -19,6 +172,8 @@ interface Props { selectedItemRef: React.RefObject; databaseUri: string; sourceLocationPrefix: string; + run?: Run; + userSettings: UserSettings; updateSelectionCallback: ( resultKey: PathNode | ResultKeysResult | undefined, ) => void; @@ -34,6 +189,8 @@ export function AlertTablePathNodeRow(props: Props) { selectedItemRef, databaseUri, sourceLocationPrefix, + run, + userSettings, updateSelectionCallback, } = props; @@ -86,7 +243,21 @@ export function AlertTablePathNodeRow(props: Props) { "[no location]" )} - {"model"} + + {userSettings.shouldShowProvenance ? ( +
+ {taxaLocations(step.taxa, run, handleSarifLocationClicked)} +
+ ) : ( + [] + )} + ; databaseUri: string; sourceLocationPrefix: string; + run?: Run; + userSettings: UserSettings; updateSelectionCallback: ( resultKey: PathNode | ResultKeysResult | undefined, ) => void; diff --git a/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx b/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx index 717cb1d0b..c405f3200 100644 --- a/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx +++ b/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx @@ -1,4 +1,4 @@ -import type { Result } from "sarif"; +import type { Result, Run } from "sarif"; import type { PathNode, Result as ResultKeysResult, @@ -12,6 +12,7 @@ import { useCallback, useMemo } from "react"; import { SarifLocation } from "./locations/SarifLocation"; import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations"; import { AlertTablePathRow } from "./AlertTablePathRow"; +import type { UserSettings } from "../../common/interface-types"; interface Props { result: Result; @@ -21,6 +22,8 @@ interface Props { selectedItemRef: React.RefObject; databaseUri: string; sourceLocationPrefix: string; + run?: Run; + userSettings: UserSettings; updateSelectionCallback: ( resultKey: PathNode | ResultKeysResult | undefined, ) => void; diff --git a/extensions/ql-vscode/src/view/results/ResultTable.tsx b/extensions/ql-vscode/src/view/results/ResultTable.tsx index 6e7c1adf5..b6718dd36 100644 --- a/extensions/ql-vscode/src/view/results/ResultTable.tsx +++ b/extensions/ql-vscode/src/view/results/ResultTable.tsx @@ -6,7 +6,7 @@ import { AlertTableNoResults } from "./AlertTableNoResults"; import { AlertTableHeader } from "./AlertTableHeader"; export function ResultTable(props: ResultTableProps) { - const { resultSet } = props; + const { resultSet, userSettings } = props; switch (resultSet.t) { case "RawResultSet": return ; @@ -21,6 +21,8 @@ export function ResultTable(props: ResultTableProps) { sourceLocationPrefix={ resultSet.interpretation.sourceLocationPrefix } + run={data.runs[0]} + userSettings={userSettings} numTruncatedResults={resultSet.interpretation.numTruncatedResults} header={} noResults={ diff --git a/extensions/ql-vscode/src/view/results/ResultTables.tsx b/extensions/ql-vscode/src/view/results/ResultTables.tsx index ba114bdf4..e84504462 100644 --- a/extensions/ql-vscode/src/view/results/ResultTables.tsx +++ b/extensions/ql-vscode/src/view/results/ResultTables.tsx @@ -8,6 +8,7 @@ import type { ResultSet, ParsedResultSets, IntoResultsViewMsg, + UserSettings, } from "../../common/interface-types"; import { ALERTS_TABLE_NAME, @@ -33,6 +34,7 @@ interface ResultTablesProps { rawResultSets: readonly ResultSet[]; interpretation: Interpretation | undefined; database: DatabaseInfo; + userSettings: UserSettings; metadata?: QueryMetadata; resultsPath: string; origResultsPaths: ResultsPaths; @@ -94,6 +96,7 @@ export function ResultTables(props: ResultTablesProps) { interpretation, database, resultsPath, + userSettings, metadata, origResultsPaths, isLoadingNewResults, @@ -242,6 +245,7 @@ export function ResultTables(props: ResultTablesProps) { ({ + shouldShowProvenance: false, + }); + const updateStateWithNewResultsInfo = useCallback( (resultsInfo: ResultsInfo): void => { let results: Results | null = null; @@ -110,6 +115,10 @@ export function ResultsApp() { const handleMessage = useCallback( (msg: IntoResultsViewMsg): void => { switch (msg.t) { + case "setUserSettings": + setUserSettings(msg.userSettings); + break; + case "setState": updateStateWithNewResultsInfo({ resultsPath: msg.resultsPath, @@ -217,6 +226,7 @@ export function ResultsApp() { ? displayedResults.resultsInfo.interpretation : undefined } + userSettings={userSettings} database={displayedResults.results.database} origResultsPaths={displayedResults.resultsInfo.origResultsPaths} resultsPath={displayedResults.resultsInfo.resultsPath} diff --git a/extensions/ql-vscode/src/view/results/locations/ClickableLocation.tsx b/extensions/ql-vscode/src/view/results/locations/ClickableLocation.tsx index 29f62c72f..156c9f1ce 100644 --- a/extensions/ql-vscode/src/view/results/locations/ClickableLocation.tsx +++ b/extensions/ql-vscode/src/view/results/locations/ClickableLocation.tsx @@ -7,7 +7,7 @@ import type { UrlValueResolvable } from "../../../common/raw-result-types"; interface Props { loc: UrlValueResolvable; label: string; - databaseUri: string; + databaseUri: string | undefined; onClick?: () => void; } diff --git a/extensions/ql-vscode/src/view/results/locations/Location.tsx b/extensions/ql-vscode/src/view/results/locations/Location.tsx index 85a325aeb..ca5967a23 100644 --- a/extensions/ql-vscode/src/view/results/locations/Location.tsx +++ b/extensions/ql-vscode/src/view/results/locations/Location.tsx @@ -26,17 +26,13 @@ export function Location({ const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]); if (loc === undefined) { - return ; + return ; } if (loc.type === "string") { return {loc.value}; } - if (databaseUri === undefined) { - return ; - } - return ( void; } diff --git a/extensions/ql-vscode/src/view/results/result-table-utils.ts b/extensions/ql-vscode/src/view/results/result-table-utils.ts index 2a6f243a1..8e3403242 100644 --- a/extensions/ql-vscode/src/view/results/result-table-utils.ts +++ b/extensions/ql-vscode/src/view/results/result-table-utils.ts @@ -2,6 +2,7 @@ import type { QueryMetadata, RawResultsSortState, ResultSet, + UserSettings, } from "../../common/interface-types"; import { SortDirection } from "../../common/interface-types"; import { assertNever } from "../../common/helpers-pure"; @@ -11,6 +12,7 @@ import type { UrlValueResolvable } from "../../common/raw-result-types"; export interface ResultTableProps { resultSet: ResultSet; databaseUri: string; + userSettings: UserSettings; metadata?: QueryMetadata; resultsPath: string | undefined; sortState?: RawResultsSortState; @@ -41,7 +43,7 @@ export const selectedRowClassName = "vscode-codeql__result-table-row--selected"; export function jumpToLocation( loc: UrlValueResolvable, - databaseUri: string, + databaseUri: string | undefined, ): void { vscode.postMessage({ t: "viewSourceFile", diff --git a/extensions/ql-vscode/src/view/results/resultsView.css b/extensions/ql-vscode/src/view/results/resultsView.css index 8cc6ed1b7..4cfeb3ce9 100644 --- a/extensions/ql-vscode/src/view/results/resultsView.css +++ b/extensions/ql-vscode/src/view/results/resultsView.css @@ -144,3 +144,12 @@ td.vscode-codeql__path-index-cell { .vscode-codeql__location-cell { text-align: right !important; } + +.vscode-codeql__taxa-cell { + text-align: left !important; +} + +.vscode-codeql__taxa-cell-div { + background-color: transparent; + display: grid; +}