Swift 代码的基本查询¶
学习使用 CodeQL 扩展在 Visual Studio Code 中编写和运行简单的 CodeQL 查询。
注意
目前,针对 Swift 的 CodeQL 分析处于测试阶段。在测试阶段,对 Swift 代码的分析以及相应的文档将不如其他语言全面。
有关安装适用于 Visual Studio Code 的 CodeQL 扩展的信息,请参阅“安装适用于 Visual Studio Code 的 CodeQL”。
关于查询¶
我们将要运行的查询对代码执行基本搜索,查找 if 表达式,这些表达式是冗余的,因为它们具有空的 then 分支。例如,以下代码:
if error {
// we should handle the error
}
查找用于实验的 CodeQL 数据库¶
在开始为 Swift 代码编写查询之前,您需要一个 CodeQL 数据库来运行这些查询。最简单的方法是直接从 GitHub.com 下载使用 Swift 的存储库的数据库。
- 在 Visual Studio Code 中,单击左侧边栏中的 **QL** 图标
以显示 CodeQL 扩展。 - 单击 **从 GitHub 获取** 或 GitHub 徽标
(位于 CodeQL 扩展的顶部)以打开一个条目字段。 - 将存储库的 URL 复制到该字段,然后按键盘上的 **Enter** 键。例如,https://github.com/alamofire/alamofire。
- 可选地,如果存储库有多个可用的 CodeQL 数据库,请选择
swift以下载从 Swift 代码创建的数据库。
有关数据库下载进度的信息显示在 Visual Studio Code 的右下角。下载完成后,数据库将显示在 CodeQL 扩展的 **数据库** 部分中,并带有复选标记(见下图)。
运行快速查询¶
适用于 Visual Studio Code 的 CodeQL 扩展将几个 **CodeQL:** 命令添加到命令面板,包括 **快速查询**,您可以使用此命令在没有任何设置的情况下运行查询。
从 Visual Studio Code 中的命令面板中,选择 **CodeQL: 快速查询**。
片刻之后,将打开一个新的选项卡 quick-query.ql,您可以在其中编写针对当前选定 CodeQL 数据库(此处为
swift数据库)的查询。如果系统提示您将工作区重新加载为多文件夹工作区以允许快速查询,请接受或使用入门工作流创建新的工作区。
在快速查询选项卡中,删除内容,然后粘贴以下查询。
import swift from IfStmt ifStmt where ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0 select ifStmt, "This 'if' statement is redundant."
将查询保存到其默认位置(工作区下的临时“快速查询”目录,用于
GitHub.vscode-codeql/quick-queries)。右键单击查询选项卡,然后选择 **CodeQL: 在所选数据库上运行查询**。(或者,从命令面板运行命令。)
查询将花费几分钟时间才能返回结果。查询完成后,结果将显示在 CodeQL 查询结果视图中,位于主编辑器视图旁边。
查询结果列在两列中,分别对应于查询的
select子句中的表达式。第一列对应于表达式ifStmt,并链接到项目源代码中ifStmt出现的位置。第二列是警报消息。
如果找到任何匹配的代码,请单击 ifStmt 列中的链接以打开文件并突出显示匹配的 if 语句。
注意
如果您想将实验性查询移动到更永久的位置,则需要移动整个
Quick Queries目录。该目录是一个 CodeQL 包,包含一个qlpack.yml文件,该文件将内容定义为适用于 Swift CodeQL 数据库的查询。有关 CodeQL 包的更多信息,请参阅“管理 CodeQL 查询包和库包”。
关于查询结构¶
在初始 import 语句之后,这个简单的查询包含三个部分,这些部分的作用类似于 SQL 查询的 FROM、WHERE 和 SELECT 部分。
| 查询部分 | 目的 | 详细信息 |
|---|---|---|
import swift |
导入 Swift 的标准 CodeQL AST 库。 | 每个查询都以一个或多个 import 语句开头。 |
from IfStmt ifStmt |
定义查询的变量。声明的形式为:<type> <variable name> |
我们使用:一个 IfStmt 变量,用于 if 语句。 |
where ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0 |
定义对变量的条件。 | ifStmt.getThen():获取 if 表达式的 then 分支。 .(BraceStmt):要求 then 分支是花括号语句 ({ })。 .getNumberOfElements() = 0:要求花括号语句不包含任何子语句。 |
select ifStmt, "This 'if' statement is redundant." |
定义要为每个匹配项报告的内容。
|
报告结果 if 语句以及一个解释问题的字符串。 |
扩展查询¶
查询编写本质上是一个迭代过程。您编写一个简单的查询,然后在运行查询时,您会发现以前没有考虑过的示例或改进的机会。
删除误报结果¶
浏览基本查询的结果表明,它可以改进。在结果中,您可能会发现 if 语句的示例,这些语句具有 else 分支,其中空 then 分支确实起作用。例如
if (option == "-verbose") {
// nothing to do - handled earlier
} else {
handleError("unrecognized option")
}
在这种情况下,将具有空 then 分支的 if 语句标识为冗余是一个误报。解决此问题的一种方法是修改查询,使其选择 if 语句,其中 then 和 else 分支均不存在。
要排除具有 else 分支的 if 语句
将以下内容添加到 where 子句中
and not exists(ifStmt.getElse())
现在,
where子句为where ifStmt.getThen().(BraceStmt).getNumberOfElements() = 0 and not exists(ifStmt.getElse())
重新运行查询。
现在,结果更少了,因为不再包含具有
else分支的if表达式。