C 和 C++ 代码的基本查询¶
学习使用带有 CodeQL 扩展的 Visual Studio Code 编写和运行简单的 CodeQL 查询。
有关安装 Visual Studio Code 的 CodeQL 扩展的信息,请参见“安装 VS Code 的 CodeQL”。
查找可用于实验的 CodeQL 数据库¶
在开始为 C/C++ 代码编写查询之前,您需要一个 CodeQL 数据库来运行这些查询。最简单的方法是从 GitHub.com 直接下载使用 C/C++ 的存储库的数据库。
- 在 Visual Studio Code 中,单击左侧边栏中的 **QL** 图标
以显示 CodeQL 扩展。
- 单击 CodeQL 扩展顶部的 **从 GitHub** 或 GitHub 徽标
以打开一个条目字段。
- 将存储库的 URL 复制到该字段中,然后按键盘 **Enter** 键。例如,https://github.com/protocolbuffers/protobuf。
- 可选地,如果存储库有多个可用的 CodeQL 数据库,请选择
cpp
以下载从 C/C++ 代码创建的数据库。
有关数据库下载进度的信息显示在 Visual Studio Code 的右下角。下载完成后,数据库将在 CodeQL 扩展的 **数据库** 部分中显示一个选中标记(参见下图)。
运行快速查询¶
VS Code 的 CodeQL 扩展将多个 **CodeQL:** 命令添加到命令面板,包括 **快速查询**,您可以使用该命令运行查询,而无需任何设置。
从 VS Code 中的命令面板中,选择 **CodeQL: 快速查询**。
片刻之后,将打开一个名为 *quick-query.ql* 的新选项卡,您可以随时准备为当前选择的 CodeQL 数据库(此处为
cpp
数据库)编写查询。如果系统提示您将工作区重新加载为多文件夹工作区以允许快速查询,请接受或使用入门工作流创建一个新工作区。
在快速查询选项卡中,删除
select ""
并将以下查询粘贴到导入语句import cpp
下面。from IfStmt ifstmt, BlockStmt block where ifstmt.getThen() = block and block.getNumStmt() = 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
文件,该文件将内容定义为 C/C++ CodeQL 数据库的查询。有关 CodeQL 包的更多信息,请参见“管理 CodeQL 查询包和库包”。
关于查询结构¶
在初始 import
语句之后,这个简单的查询包含三个部分,它们的功能类似于 SQL 查询的 FROM、WHERE 和 SELECT 部分。
查询部分 | 目的 | 详细信息 |
---|---|---|
import cpp |
导入 C/C++ 的标准 CodeQL 库。 | 每个查询都以一个或多个 import 语句开头。 |
from IfStmt ifstmt, BlockStmt block |
定义查询的变量。声明采用以下形式:<type> <variable name> |
我们使用
|
where ifstmt.getThen() = block and block.getNumStmt() = 0 |
定义变量的条件。 |
|
select ifstmt, "This 'if' statement is redundant." |
定义每个匹配项要报告的内容。 用于查找不良编码实践实例的查询的 |
报告生成的 if 语句,并使用一个字符串来解释问题。 |
扩展查询¶
查询编写本质上是一个迭代过程。您编写一个简单的查询,然后在运行查询时,您会发现以前没有考虑过的示例,或者改进的机会。
删除误报结果¶
浏览基本查询的结果表明,它可以改进。在结果中,您可能会发现一些 if
语句的示例,这些语句具有 else
分支,其中空 then
分支确实起作用。例如
if (...) {
...
} else if (!strcmp(option, "-verbose")) {
// nothing to do - handled earlier
} else {
error("unrecognized option");
}
在这种情况下,将具有空 then
分支的 if
语句识别为多余的,这是一个误报。对此的一种解决方案是修改查询,以忽略具有 else
分支的 if
语句的空 then
分支。
要排除具有 else
分支的 if
语句
编辑您的查询,并将
where
子句扩展为包括以下附加条件and not ifstmt.hasElse()
现在,
where
子句为where ifstmt.getThen() = block and block.getNumStmt() = 0 and not ifstmt.hasElse()
重新运行查询。
现在结果更少了,因为不再报告具有
else
分支的if
语句。