diff --git a/.github/codeql/queries/unique-command-use.ql b/.github/codeql/queries/unique-command-use.ql index 7c1d9e196..a882250e6 100644 --- a/.github/codeql/queries/unique-command-use.ql +++ b/.github/codeql/queries/unique-command-use.ql @@ -15,134 +15,145 @@ * that should be changed to fix the alert. */ - import javascript +import javascript - /** - * The name of a VS Code command. - */ - class CommandName extends string { - CommandName() { exists(CommandUsage e | e.getCommandName() = this) } - - /** - * In how many ways is this command used. Will always be at least 1. - */ - int getNumberOfUsages() { result = count(this.getAUse()) } +/** + * The name of a VS Code command. + */ +class CommandName extends string { + CommandName() { exists(CommandUsage e | e.getCommandName() = this) } - /** - * Get a usage of this command. - */ - CommandUsage getAUse() { result.getCommandName() = this } - - /** - * Get the canonical first usage of this command, to use for the location - * of the alert. The implementation of this ordering of usages is arbitrary - * and the usage given may not be the one that should be changed when fixing - * the alert. - */ - CommandUsage getFirstUsage() { - result = - max(CommandUsage use | - use = this.getAUse() - | - use - order by - use.getFile().getRelativePath(), use.getLocation().getStartLine(), - use.getLocation().getStartColumn() - ) - } - } - - /** - * Represents a single usage of a command, either from within code or - * from the command's definition in package.json - */ - abstract class CommandUsage extends Locatable { - abstract string getCommandName(); - } - - /** - * A usage of a command from the typescript code, by calling `executeCommand`. - */ - class CommandUsageCallExpr extends CommandUsage, CallExpr { - CommandUsageCallExpr() { - this.getCalleeName() = "executeCommand" and - this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and - not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%") - } - - override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() } - } + /** + * In how many ways is this command used. Will always be at least 1. + */ + int getNumberOfUsages() { result = count(this.getAUse()) } - /** - * A usage of a command from the typescript code, by calling `CommandManager.execute`. - */ - class CommandUsageCommandManagerMethodCallExpr extends CommandUsage, MethodCallExpr { - CommandUsageCommandManagerMethodCallExpr() { - this.getCalleeName() = "execute" and - this.getReceiver().getType().unfold().(TypeReference).getTypeName().getName() = "CommandManager" and - this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and - not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%") - } + /** + * Get a usage of this command. + */ + CommandUsage getAUse() { result.getCommandName() = this } - override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() } - } - - /** - * A usage of a command from any menu that isn't the command palette. - * This means a user could invoke the command by clicking on a button in - * something like a menu or a dropdown. - */ - class CommandUsagePackageJsonMenuItem extends CommandUsage, JsonObject { - CommandUsagePackageJsonMenuItem() { - exists(this.getPropValue("command")) and - exists(PackageJson packageJson, string menuName | - packageJson - .getPropValue("contributes") - .getPropValue("menus") - .getPropValue(menuName) - .getElementValue(_) = this and - menuName != "commandPalette" - ) - } - - override string getCommandName() { result = this.getPropValue("command").getStringValue() } - } - - /** - * Is the given command disabled for use in the command palette by - * a block with a `"when": "false"` field. - */ - predicate isDisabledInCommandPalette(string commandName) { - exists(PackageJson packageJson, JsonObject commandPaletteObject | - packageJson - .getPropValue("contributes") - .getPropValue("menus") - .getPropValue("commandPalette") - .getElementValue(_) = commandPaletteObject and - commandPaletteObject.getPropValue("command").getStringValue() = commandName and - commandPaletteObject.getPropValue("when").getStringValue() = "false" - ) - } - - /** - * Represents a command being usable from the command palette. - * This means that a user could choose to manually invoke the command. - */ - class CommandUsagePackageJsonCommandPalette extends CommandUsage, JsonObject { - CommandUsagePackageJsonCommandPalette() { - this.getFile().getBaseName() = "package.json" and - exists(this.getPropValue("command")) and - exists(PackageJson packageJson | - packageJson.getPropValue("contributes").getPropValue("commands").getElementValue(_) = this - ) and - not isDisabledInCommandPalette(this.getPropValue("command").getStringValue()) - } - - override string getCommandName() { result = this.getPropValue("command").getStringValue() } - } - - from CommandName c - where c.getNumberOfUsages() > 1 - select c.getFirstUsage(), - "The " + c + " command is used from " + c.getNumberOfUsages() + " locations" - \ No newline at end of file + /** + * Get the canonical first usage of this command, to use for the location + * of the alert. The implementation of this ordering of usages is arbitrary + * and the usage given may not be the one that should be changed when fixing + * the alert. + */ + CommandUsage getFirstUsage() { + result = + max(CommandUsage use | + use = this.getAUse() + | + use + order by + use.getFile().getRelativePath(), use.getLocation().getStartLine(), + use.getLocation().getStartColumn() + ) + } +} + +/** + * Matches one of the members of `BuiltInVsCodeCommands` from `extensions/ql-vscode/src/common/commands.ts`. + */ +class BuiltInVSCodeCommand extends string { + BuiltInVSCodeCommand() { + exists(TypeAliasDeclaration tad | + tad.getIdentifier().getName() = "BuiltInVsCodeCommands" and + tad.getDefinition().(InterfaceTypeExpr).getAMember().getName() = this + ) + } +} + +/** + * Represents a single usage of a command, either from within code or + * from the command's definition in package.json + */ +abstract class CommandUsage extends Locatable { + abstract string getCommandName(); +} + +/** + * A usage of a command from the typescript code, by calling `executeCommand`. + */ +class CommandUsageCallExpr extends CommandUsage, CallExpr { + CommandUsageCallExpr() { + this.getCalleeName() = "executeCommand" and + this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and + not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%") + } + + override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() } +} + +/** + * A usage of a command from the typescript code, by calling `CommandManager.execute`. + */ +class CommandUsageCommandManagerMethodCallExpr extends CommandUsage, MethodCallExpr { + CommandUsageCommandManagerMethodCallExpr() { + this.getCalleeName() = "execute" and + this.getReceiver().getType().unfold().(TypeReference).getTypeName().getName() = "CommandManager" and + this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and + not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%") + } + + override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() } +} + +/** + * A usage of a command from any menu that isn't the command palette. + * This means a user could invoke the command by clicking on a button in + * something like a menu or a dropdown. + */ +class CommandUsagePackageJsonMenuItem extends CommandUsage, JsonObject { + CommandUsagePackageJsonMenuItem() { + exists(this.getPropValue("command")) and + exists(PackageJson packageJson, string menuName | + packageJson + .getPropValue("contributes") + .getPropValue("menus") + .getPropValue(menuName) + .getElementValue(_) = this and + menuName != "commandPalette" + ) + } + + override string getCommandName() { result = this.getPropValue("command").getStringValue() } +} + +/** + * Is the given command disabled for use in the command palette by + * a block with a `"when": "false"` field. + */ +predicate isDisabledInCommandPalette(string commandName) { + exists(PackageJson packageJson, JsonObject commandPaletteObject | + packageJson + .getPropValue("contributes") + .getPropValue("menus") + .getPropValue("commandPalette") + .getElementValue(_) = commandPaletteObject and + commandPaletteObject.getPropValue("command").getStringValue() = commandName and + commandPaletteObject.getPropValue("when").getStringValue() = "false" + ) +} + +/** + * Represents a command being usable from the command palette. + * This means that a user could choose to manually invoke the command. + */ +class CommandUsagePackageJsonCommandPalette extends CommandUsage, JsonObject { + CommandUsagePackageJsonCommandPalette() { + this.getFile().getBaseName() = "package.json" and + exists(this.getPropValue("command")) and + exists(PackageJson packageJson | + packageJson.getPropValue("contributes").getPropValue("commands").getElementValue(_) = this + ) and + not isDisabledInCommandPalette(this.getPropValue("command").getStringValue()) + } + + override string getCommandName() { result = this.getPropValue("command").getStringValue() } +} + +from CommandName c +where c.getNumberOfUsages() > 1 and not c instanceof BuiltInVSCodeCommand +select c.getFirstUsage(), + "The " + c + " command is used from " + c.getNumberOfUsages() + " locations"