Skip to content

Commit ca16dca

Browse files
Merge pull request #2645 from github/robertbrignull/fix_alert_546
Don't alert for multiple usages of built-in VS Code commands
2 parents 396dc3e + aacc243 commit ca16dca

File tree

1 file changed

+142
-131
lines changed

1 file changed

+142
-131
lines changed

.github/codeql/queries/unique-command-use.ql

Lines changed: 142 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -15,134 +15,145 @@
1515
* that should be changed to fix the alert.
1616
*/
1717

18-
import javascript
19-
20-
/**
21-
* The name of a VS Code command.
22-
*/
23-
class CommandName extends string {
24-
CommandName() { exists(CommandUsage e | e.getCommandName() = this) }
25-
26-
/**
27-
* In how many ways is this command used. Will always be at least 1.
28-
*/
29-
int getNumberOfUsages() { result = count(this.getAUse()) }
30-
31-
/**
32-
* Get a usage of this command.
33-
*/
34-
CommandUsage getAUse() { result.getCommandName() = this }
35-
36-
/**
37-
* Get the canonical first usage of this command, to use for the location
38-
* of the alert. The implementation of this ordering of usages is arbitrary
39-
* and the usage given may not be the one that should be changed when fixing
40-
* the alert.
41-
*/
42-
CommandUsage getFirstUsage() {
43-
result =
44-
max(CommandUsage use |
45-
use = this.getAUse()
46-
|
47-
use
48-
order by
49-
use.getFile().getRelativePath(), use.getLocation().getStartLine(),
50-
use.getLocation().getStartColumn()
51-
)
52-
}
53-
}
54-
55-
/**
56-
* Represents a single usage of a command, either from within code or
57-
* from the command's definition in package.json
58-
*/
59-
abstract class CommandUsage extends Locatable {
60-
abstract string getCommandName();
61-
}
62-
63-
/**
64-
* A usage of a command from the typescript code, by calling `executeCommand`.
65-
*/
66-
class CommandUsageCallExpr extends CommandUsage, CallExpr {
67-
CommandUsageCallExpr() {
68-
this.getCalleeName() = "executeCommand" and
69-
this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and
70-
not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%")
71-
}
72-
73-
override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() }
74-
}
75-
76-
/**
77-
* A usage of a command from the typescript code, by calling `CommandManager.execute`.
78-
*/
79-
class CommandUsageCommandManagerMethodCallExpr extends CommandUsage, MethodCallExpr {
80-
CommandUsageCommandManagerMethodCallExpr() {
81-
this.getCalleeName() = "execute" and
82-
this.getReceiver().getType().unfold().(TypeReference).getTypeName().getName() = "CommandManager" and
83-
this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and
84-
not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%")
85-
}
86-
87-
override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() }
88-
}
89-
90-
/**
91-
* A usage of a command from any menu that isn't the command palette.
92-
* This means a user could invoke the command by clicking on a button in
93-
* something like a menu or a dropdown.
94-
*/
95-
class CommandUsagePackageJsonMenuItem extends CommandUsage, JsonObject {
96-
CommandUsagePackageJsonMenuItem() {
97-
exists(this.getPropValue("command")) and
98-
exists(PackageJson packageJson, string menuName |
99-
packageJson
100-
.getPropValue("contributes")
101-
.getPropValue("menus")
102-
.getPropValue(menuName)
103-
.getElementValue(_) = this and
104-
menuName != "commandPalette"
105-
)
106-
}
107-
108-
override string getCommandName() { result = this.getPropValue("command").getStringValue() }
109-
}
110-
111-
/**
112-
* Is the given command disabled for use in the command palette by
113-
* a block with a `"when": "false"` field.
114-
*/
115-
predicate isDisabledInCommandPalette(string commandName) {
116-
exists(PackageJson packageJson, JsonObject commandPaletteObject |
117-
packageJson
118-
.getPropValue("contributes")
119-
.getPropValue("menus")
120-
.getPropValue("commandPalette")
121-
.getElementValue(_) = commandPaletteObject and
122-
commandPaletteObject.getPropValue("command").getStringValue() = commandName and
123-
commandPaletteObject.getPropValue("when").getStringValue() = "false"
124-
)
125-
}
126-
127-
/**
128-
* Represents a command being usable from the command palette.
129-
* This means that a user could choose to manually invoke the command.
130-
*/
131-
class CommandUsagePackageJsonCommandPalette extends CommandUsage, JsonObject {
132-
CommandUsagePackageJsonCommandPalette() {
133-
this.getFile().getBaseName() = "package.json" and
134-
exists(this.getPropValue("command")) and
135-
exists(PackageJson packageJson |
136-
packageJson.getPropValue("contributes").getPropValue("commands").getElementValue(_) = this
137-
) and
138-
not isDisabledInCommandPalette(this.getPropValue("command").getStringValue())
139-
}
140-
141-
override string getCommandName() { result = this.getPropValue("command").getStringValue() }
142-
}
143-
144-
from CommandName c
145-
where c.getNumberOfUsages() > 1
146-
select c.getFirstUsage(),
147-
"The " + c + " command is used from " + c.getNumberOfUsages() + " locations"
148-
18+
import javascript
19+
20+
/**
21+
* The name of a VS Code command.
22+
*/
23+
class CommandName extends string {
24+
CommandName() { exists(CommandUsage e | e.getCommandName() = this) }
25+
26+
/**
27+
* In how many ways is this command used. Will always be at least 1.
28+
*/
29+
int getNumberOfUsages() { result = count(this.getAUse()) }
30+
31+
/**
32+
* Get a usage of this command.
33+
*/
34+
CommandUsage getAUse() { result.getCommandName() = this }
35+
36+
/**
37+
* Get the canonical first usage of this command, to use for the location
38+
* of the alert. The implementation of this ordering of usages is arbitrary
39+
* and the usage given may not be the one that should be changed when fixing
40+
* the alert.
41+
*/
42+
CommandUsage getFirstUsage() {
43+
result =
44+
max(CommandUsage use |
45+
use = this.getAUse()
46+
|
47+
use
48+
order by
49+
use.getFile().getRelativePath(), use.getLocation().getStartLine(),
50+
use.getLocation().getStartColumn()
51+
)
52+
}
53+
}
54+
55+
/**
56+
* Matches one of the members of `BuiltInVsCodeCommands` from `extensions/ql-vscode/src/common/commands.ts`.
57+
*/
58+
class BuiltInVSCodeCommand extends string {
59+
BuiltInVSCodeCommand() {
60+
exists(TypeAliasDeclaration tad |
61+
tad.getIdentifier().getName() = "BuiltInVsCodeCommands" and
62+
tad.getDefinition().(InterfaceTypeExpr).getAMember().getName() = this
63+
)
64+
}
65+
}
66+
67+
/**
68+
* Represents a single usage of a command, either from within code or
69+
* from the command's definition in package.json
70+
*/
71+
abstract class CommandUsage extends Locatable {
72+
abstract string getCommandName();
73+
}
74+
75+
/**
76+
* A usage of a command from the typescript code, by calling `executeCommand`.
77+
*/
78+
class CommandUsageCallExpr extends CommandUsage, CallExpr {
79+
CommandUsageCallExpr() {
80+
this.getCalleeName() = "executeCommand" and
81+
this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and
82+
not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%")
83+
}
84+
85+
override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() }
86+
}
87+
88+
/**
89+
* A usage of a command from the typescript code, by calling `CommandManager.execute`.
90+
*/
91+
class CommandUsageCommandManagerMethodCallExpr extends CommandUsage, MethodCallExpr {
92+
CommandUsageCommandManagerMethodCallExpr() {
93+
this.getCalleeName() = "execute" and
94+
this.getReceiver().getType().unfold().(TypeReference).getTypeName().getName() = "CommandManager" and
95+
this.getArgument(0).(StringLiteral).getValue().matches("%codeQL%") and
96+
not this.getFile().getRelativePath().matches("extensions/ql-vscode/test/%")
97+
}
98+
99+
override string getCommandName() { result = this.getArgument(0).(StringLiteral).getValue() }
100+
}
101+
102+
/**
103+
* A usage of a command from any menu that isn't the command palette.
104+
* This means a user could invoke the command by clicking on a button in
105+
* something like a menu or a dropdown.
106+
*/
107+
class CommandUsagePackageJsonMenuItem extends CommandUsage, JsonObject {
108+
CommandUsagePackageJsonMenuItem() {
109+
exists(this.getPropValue("command")) and
110+
exists(PackageJson packageJson, string menuName |
111+
packageJson
112+
.getPropValue("contributes")
113+
.getPropValue("menus")
114+
.getPropValue(menuName)
115+
.getElementValue(_) = this and
116+
menuName != "commandPalette"
117+
)
118+
}
119+
120+
override string getCommandName() { result = this.getPropValue("command").getStringValue() }
121+
}
122+
123+
/**
124+
* Is the given command disabled for use in the command palette by
125+
* a block with a `"when": "false"` field.
126+
*/
127+
predicate isDisabledInCommandPalette(string commandName) {
128+
exists(PackageJson packageJson, JsonObject commandPaletteObject |
129+
packageJson
130+
.getPropValue("contributes")
131+
.getPropValue("menus")
132+
.getPropValue("commandPalette")
133+
.getElementValue(_) = commandPaletteObject and
134+
commandPaletteObject.getPropValue("command").getStringValue() = commandName and
135+
commandPaletteObject.getPropValue("when").getStringValue() = "false"
136+
)
137+
}
138+
139+
/**
140+
* Represents a command being usable from the command palette.
141+
* This means that a user could choose to manually invoke the command.
142+
*/
143+
class CommandUsagePackageJsonCommandPalette extends CommandUsage, JsonObject {
144+
CommandUsagePackageJsonCommandPalette() {
145+
this.getFile().getBaseName() = "package.json" and
146+
exists(this.getPropValue("command")) and
147+
exists(PackageJson packageJson |
148+
packageJson.getPropValue("contributes").getPropValue("commands").getElementValue(_) = this
149+
) and
150+
not isDisabledInCommandPalette(this.getPropValue("command").getStringValue())
151+
}
152+
153+
override string getCommandName() { result = this.getPropValue("command").getStringValue() }
154+
}
155+
156+
from CommandName c
157+
where c.getNumberOfUsages() > 1 and not c instanceof BuiltInVSCodeCommand
158+
select c.getFirstUsage(),
159+
"The " + c + " command is used from " + c.getNumberOfUsages() + " locations"

0 commit comments

Comments
 (0)