|
| 1 | +import { StringLocation, LocationValue, LocationStyle, ResolvableLocationValue } from './bqrs-types'; |
| 2 | + |
| 3 | +/** |
| 4 | + * The CodeQL filesystem libraries use this pattern in `getURL()` predicates |
| 5 | + * to describe the location of an entire filesystem resource. |
| 6 | + * Such locations appear as `StringLocation`s instead of `FivePartLocation`s. |
| 7 | + * |
| 8 | + * Folder resources also get similar URLs, but with the `folder` scheme. |
| 9 | + * They are deliberately ignored here, since there is no suitable location to show the user. |
| 10 | + */ |
| 11 | +const FILE_LOCATION_REGEX = /file:\/\/(.+):([0-9]+):([0-9]+):([0-9]+):([0-9]+)/; |
| 12 | +/** |
| 13 | + * Gets a resolvable source file location for the specified `LocationValue`, if possible. |
| 14 | + * @param loc The location to test. |
| 15 | + */ |
| 16 | +export function tryGetResolvableLocation( |
| 17 | + loc: LocationValue | undefined |
| 18 | +): ResolvableLocationValue | undefined { |
| 19 | + if (loc === undefined) { |
| 20 | + return undefined; |
| 21 | + } else if (loc.t === LocationStyle.FivePart && loc.file) { |
| 22 | + return loc; |
| 23 | + } else if (loc.t === LocationStyle.WholeFile && loc.file) { |
| 24 | + return loc; |
| 25 | + } else if (loc.t === LocationStyle.String && loc.loc) { |
| 26 | + return tryGetLocationFromString(loc); |
| 27 | + } else { |
| 28 | + return undefined; |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +export function tryGetLocationFromString( |
| 33 | + loc: StringLocation |
| 34 | +): ResolvableLocationValue | undefined { |
| 35 | + const matches = FILE_LOCATION_REGEX.exec(loc.loc); |
| 36 | + if (matches && matches.length > 1 && matches[1]) { |
| 37 | + if (isWholeFileMatch(matches)) { |
| 38 | + return { |
| 39 | + t: LocationStyle.WholeFile, |
| 40 | + file: matches[1], |
| 41 | + }; |
| 42 | + } else { |
| 43 | + return { |
| 44 | + t: LocationStyle.FivePart, |
| 45 | + file: matches[1], |
| 46 | + lineStart: Number(matches[2]), |
| 47 | + colStart: Number(matches[3]), |
| 48 | + lineEnd: Number(matches[4]), |
| 49 | + colEnd: Number(matches[5]), |
| 50 | + } |
| 51 | + } |
| 52 | + } else { |
| 53 | + return undefined; |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +function isWholeFileMatch(matches: RegExpExecArray): boolean { |
| 58 | + return ( |
| 59 | + matches[2] === "0" && |
| 60 | + matches[3] === "0" && |
| 61 | + matches[4] === "0" && |
| 62 | + matches[5] === "0" |
| 63 | + ); |
| 64 | +} |
0 commit comments