CodeQL 文档

注释

注释是在 QL 实体或名称声明之前直接放置的字符串。

例如,要将模块 M 声明为私有,您可以使用

private module M {
    ...
}
请注意,一些注释作用于实体本身,而另一些则作用于实体的特定名称
  • 作用于实体: abstractcachedexternaltransientoverridepragmalanguagebindingset
  • 作用于名称: deprecatedlibraryprivatefinalquery

例如,如果您使用 private 注释实体,则只有该特定名称是私有的。您仍然可以使用其他名称(使用 别名)访问该实体。另一方面,如果您使用 cached 注释实体,则实体本身将被缓存。

以下是一个明确的示例

module M {
  private int foo() { result = 1 }
  predicate bar = foo/0;
}

在这种情况下,查询 select M::foo() 会导致编译器错误,因为名称 foo 是私有的。查询 select M::bar() 是有效的(结果为 1),因为名称 bar 是可见的,它是谓词 foo 的别名。

您可以将 cached 应用于 foo,但不能应用于 bar,因为 foo 是实体的声明。

注释概述

本节介绍了不同注释的作用以及何时可以使用它们。您还可以在 QL 语言规范 的“注释”部分找到一个摘要表。

abstract

适用于: 成员谓词

abstract 注释用于定义抽象实体。

有关抽象类的信息,请参见“”。

抽象谓词是没有任何主体的成员谓词。它们可以定义在任何类上,并且应该在非抽象子类型中被 重写

以下是一个使用抽象谓词的示例。在 QL 中编写数据流分析时,一个常见的模式是定义一个配置类。这样的配置必须描述(除其他事项外)它跟踪的数据源。所有此类配置的超类型可能如下所示

abstract class Configuration extends string {
  ...
  /** Holds if `source` is a relevant data flow source. */
  abstract predicate isSource(Node source);
  ...
}

然后,您可以定义 Configuration 的子类型,它们继承谓词 isSource,来描述特定配置。任何非抽象子类型都必须(直接或间接地)重写它来描述它们各自跟踪的数据源。

换句话说,所有扩展 Configuration 的非抽象类都必须在自己的主体中重写 isSource,或者它们必须继承自另一个重写 isSource 的类

class ConfigA extends Configuration {
  ...
  // provides a concrete definition of `isSource`
  override predicate isSource(Node source) { ... }
}
class ConfigB extends ConfigA {
  ...
  // doesn't need to override `isSource`, because it inherits it from ConfigA
}

cached

适用于: 代数数据类型特征谓词成员谓词非成员谓词模块

cached 注释指示应完整评估实体并将其存储在评估缓存中。所有对该实体的后续引用都将使用已计算的数据。这会影响来自其他查询的引用以及来自当前查询的引用。

例如,缓存评估时间很长且在许多地方重复使用的谓词可能很有用。

您应该谨慎使用 cached,因为它可能会产生意想不到的后果。例如,缓存的谓词可能会占用大量存储空间,并可能阻止 QL 编译器根据每个使用位置的上下文对谓词进行优化。但是,对于只需要计算一次谓词的权衡来说,这可能是合理的。

如果您使用 cached 注释类或模块,则其主体中的所有非 私有 实体也必须使用 cached 注释,否则编译器会报告错误。

deprecated

适用于: 代数数据类型成员谓词非成员谓词导入字段模块别名

deprecated 注释应用于过时且计划在 QL 的未来版本中删除的名称。如果您的任何 QL 文件使用过时的名称,您应该考虑将其重写为使用更新的替代方案。通常,过时的名称会有一个 QLDoc 注释,告诉用户应该使用哪个更新的元素。

例如,名称 DataFlowNode 已被弃用,并且具有以下 QLDoc 注释

/**
 * DEPRECATED: Use `DataFlow::Node` instead.
 *
 * An expression or function/class declaration,
 * viewed as a node in a data flow graph.
 */
deprecated class DataFlowNode extends @dataflownode {
  ...
}

当您在 QL 编辑器中使用名称 DataFlowNode 时,会显示此 QLDoc 注释。

external

适用于: 非成员谓词

external 注释用于谓词,以定义外部“模板”谓词。这类似于 数据库谓词

transient

适用于: 非成员谓词

transient 注释应用于也使用 external 注释的非成员谓词,以指示在评估期间不应将其缓存到磁盘。注意,如果您尝试在没有 external 的情况下应用 transient,编译器将报告错误。

final

适用于: 类型别名成员谓词字段

final 注释应用于不能被重写或扩展的名称。换句话说,最终类或最终类型别名不能作为任何其他类型的基类型,最终谓词或字段不能在子类中被重写。

如果您不希望子类更改特定实体的含义,这很有用。

例如,谓词 hasName(string name) 在元素具有名称 name 时成立。它使用谓词 getName() 来检查这一点,并且子类更改此定义没有意义。在这种情况下,hasName 应该是最终的

class Element ... {
  string getName() { result = ... }
  final predicate hasName(string name) { name = this.getName() }
}

library

适用于:

重要

此注释已弃用。与其使用 library 注释名称,不如将其放在私有(或私有导入)模块中。

library 注释应用于只能从 .qll 文件中引用的名称。如果您尝试从没有 .qll 扩展名的文件中引用该名称,则 QL 编译器会返回错误。

override

适用于: 成员谓词字段

override 注释用于指示定义 重写 基类型的成员谓词或字段。

如果您重写谓词或字段而不对其进行注释,则 QL 编译器会发出警告。

private

适用于: 代数数据类型成员谓词非成员谓词导入字段模块别名

使用 private 注释来阻止名称被导出。

如果某个名称带有 private 注释,或者它通过带有 private 注释的导入语句访问,那么您只能从当前模块的 命名空间 中引用该名称。

query

适用于非成员谓词别名

使用 query 注释将谓词(或谓词别名)变成 查询。这意味着它是 QL 程序输出的一部分。

编译器pragma

以下编译器pragma会影响查询的编译和优化。除非您遇到严重的性能问题,否则应避免使用这些注释。

在将pragma添加到代码之前,请联系 GitHub 描述性能问题。这样我们就可以为您提供针对您问题的最佳解决方案,并在改进 QL 优化器时将其考虑在内。

内联

对于简单的谓词,QL 优化器有时会用谓词体本身替换对谓词的 调用。这被称为内联

例如,假设您有一个定义 predicate one(int i) { i = 1 },并调用了该谓词 ... one(y) ...。QL 优化器可能会将谓词内联到 ... y = 1 ...

您可以使用以下编译器pragma注释来控制 QL 优化器内联谓词的方式。

pragma[inline]

适用于特征谓词成员谓词非成员谓词

使用 pragma[inline] 注释告诉 QL 优化器始终将带注释的谓词内联到调用它的位置。当谓词体非常昂贵,需要完全计算时,这可能很有用,因为它可以确保在调用谓词的位置,使用其他上下文信息对其进行评估。

pragma[inline_late]

适用于特征谓词成员谓词非成员谓词

必须将 pragma[inline_late] 注释与 bindingset[...] pragma 一起使用。它们一起告诉 QL 优化器使用指定的绑定集来评估带注释谓词体以及调用位置的连接顺序,并在连接排序后将谓词体内联到调用位置。这对于防止优化器选择次优的连接顺序可能很有用。

例如,在下面的示例中,pragma[inline_late]bindingset[x] 注释指定在 x 已绑定的上下文中,对 p 的调用应该按照连接顺序进行。这将强制连接排序器在 p(x) 之前排序 q(x),这比在 q(x) 之前排序 p(x) 更有效率。

bindingset[x]
pragma[inline_late]
predicate p(int x) { x in [0..100000000] }

predicate q(int x) { x in [0..10000] }

from int x
where p(x) and q(x)
select x

pragma[noinline]

适用于特征谓词成员谓词非成员谓词

使用 pragma[noinline] 注释来阻止谓词内联到调用它的位置。在实践中,当您已经在“辅助”谓词中将某些变量组合在一起时,该注释很有用,以确保关系作为一个整体进行评估。这有助于提高性能。QL 优化器的内联可能会撤消辅助谓词的工作,因此最好用 pragma[noinline] 为其添加注释。

pragma[nomagic]

适用于特征谓词成员谓词非成员谓词

使用 pragma[nomagic] 注释来阻止 QL 优化器对谓词执行“神奇集合”优化。

这种优化涉及从谓词 调用 的上下文中获取信息,并将其推入谓词体中。这通常是有益的,因此您不应使用 pragma[nomagic] 注释,除非 GitHub 建议您这样做。

请注意,nomagic 意味着 noinline

pragma[noopt]

适用于特征谓词成员谓词非成员谓词

使用 pragma[noopt] 注释来阻止 QL 优化器优化谓词,除非为了编译和评估正常工作而绝对必要。

这很少必要,您不应使用 pragma[noopt] 注释,除非 GitHub 建议您这样做,例如,帮助解决性能问题。

当您使用此注释时,请注意以下问题

  1. QL 优化器会自动以有效的方式对 复杂公式 的连接项进行排序。在 noopt 谓词中,连接项按您写入它们的顺序进行精确评估。

  2. QL 优化器会自动创建中间连接项,将某些公式“翻译”成 连接 的更简单公式。在 noopt 谓词中,您必须显式地编写这些连接项。特别是,您不能将谓词 调用 链在一起,也不能对 强制转换 上的谓词进行调用。您必须将它们编写为多个连接项,并显式地对它们进行排序。

    例如,假设您有以下定义

    class Small extends int {
      Small() { this in [1 .. 10] }
      Small getSucc() { result = this + 1}
    }
    
    predicate p(int i) {
      i.(Small).getSucc() = 2
    }
    
    predicate q(Small s) {
      s.getSucc().getSucc() = 3
    }
    

    如果您添加了 noopt pragma,您必须重写谓词。例如

    pragma[noopt]
    predicate p(int i) {
      exists(Small s | s = i and s.getSucc() = 2)
    }
    
    pragma[noopt]
    predicate q(Small s) {
      exists(Small succ |
        succ = s.getSucc() and
        succ.getSucc() = 3
      )
    }
    

pragma[only_bind_out]

适用于表达式

使用 pragma[only_bind_out] 注释,您可以指定 QL 编译器应该绑定表达式的方向。这对于在 QL 优化器以非有效方式对 QL 程序的某些部分进行排序的罕见情况下提高性能可能很有用。

例如,x = pragma[only_bind_out](y) 在语义上等同于 x = y,但具有不同的绑定行为。 x = yy 绑定 x,反之亦然,而 x = pragma[only_bind_out](y) 只从 y 绑定 x

有关更多信息,请参阅“绑定”。

pragma[only_bind_into]

适用于表达式

使用 pragma[only_bind_into] 注释,您可以指定 QL 编译器应该绑定表达式的方向。这对于在 QL 优化器以非有效方式对 QL 程序的某些部分进行排序的罕见情况下提高性能可能很有用。

例如,x = pragma[only_bind_into](y) 在语义上等同于 x = y,但具有不同的绑定行为。 x = yy 绑定 x,反之亦然,而 x = pragma[only_bind_into](y) 只从 x 绑定 y

有关更多信息,请参阅“绑定”。

pragma[assume_small_delta]

适用于特征谓词成员谓词非成员谓词

重要

此注释已弃用。

使用 pragma[assume_small_delta] 注释没有效果,可以安全地删除。

语言pragma

适用于模块特征谓词成员谓词非成员谓词

language[monotonicAggregates]

此注释允许您使用单调聚合,而不是标准 QL 聚合

有关更多信息,请参阅“单调聚合”。

绑定集

适用于特征谓词成员谓词非成员谓词

bindingset[...]

您可以使用此注释来显式地声明谓词或类的绑定集。绑定集是谓词或类体参数的子集,使得如果这些参数被限制为一组有限的值,那么谓词或类本身就是有限的(也就是说,它会评估为一组有限的元组)。

bindingset 注解接受以逗号分隔的变量列表。

  • 当您注释谓词时,每个变量都必须是谓词的参数,可能包括 this(对于特征谓词和成员谓词)和 result(对于返回结果的谓词)。有关更多信息,请参见“绑定行为”。
  • 当您注释类时,每个变量都必须是 this 或类中的字段。
  • ©GitHub, Inc.
  • 条款
  • 隐私