查看源代码 xref (工具 v4.1.1)
用于分析函数、模块、应用程序和发布版本之间依赖关系的交叉引用工具。
函数之间的调用要么是本地调用,例如 f()
,要么是外部调用,例如 mod:f()
。
模块数据(从 BEAM 文件中提取)包括本地函数、导出函数、本地调用和外部调用。默认情况下,对内置函数(BIF)的调用会被忽略,但是如果此模块的某些函数接受的选项 builtins
设置为 true
,则也会包含对 BIF 的调用。由分析的 OTP 版本决定哪些函数是 BIF。函数对象被假定为在其创建的位置被调用(而不是其他位置)。
未解析的调用是指对具有变量模块、变量函数或变量参数的 apply
或 spawn
的调用。例如 M:F(a)
、apply(M, f, [a])
和 spawn(m, f(), Args)
。未解析的调用表示为其中变量模块已替换为原子 '$M_EXPR'
、变量函数已替换为原子 '$F_EXPR'
、以及变量数量的参数已替换为数字 -1
的调用。上述示例表示为对 '$M_EXPR':'$F_EXPR'/1
、'$M_EXPR':f/1
和 m:'$F_EXPR'/-1
的调用。未解析的调用是外部调用的一个子集。
警告
未解析的调用会使模块数据不完整,这意味着分析结果可能无效。
应用程序是模块的集合。模块的 BEAM 文件位于应用程序目录的 ebin
子目录中。应用程序目录的名称确定应用程序的名称和版本。
发布版本是位于发布版本目录的 lib
子目录中的应用程序的集合。有关应用程序和发布版本的更多信息,请阅读《设计原则》一书。
Xref 服务器通过名称标识,这些名称在创建新服务器时提供。每个 Xref 服务器都持有一组发布版本、一组应用程序以及一组包含模块数据的模块。Xref 服务器彼此独立,所有分析都在单个 Xref 服务器的上下文中进行评估(例外情况是函数 m/1
和 d/1
,它们根本不使用服务器)。
Xref 服务器的模式决定了在将模块添加到服务器时从 BEAM 文件中提取哪些模块数据。使用选项 debug_info
编译的 BEAM 文件包含 “调试信息”,它是代码的抽象表示。
在
functions
模式(默认模式)下,函数调用和行号将从调试信息中提取。在
modules
模式下,如果存在调试信息,则会被忽略,但模块之间的依赖关系会从 BEAM 文件的其他部分提取。modules
模式比functions
模式消耗的时间和空间明显更少,但可以完成的分析受到限制。
已分析的模块是指已添加到 Xref 服务器及其模块数据的模块。库模块是指位于库路径中提到的某些目录中的模块。如果某些已分析的模块使用了库模块的某些导出函数,则该库模块被视为已使用。未知模块是指既不是已分析的模块也不是库模块,但其导出函数被某些已分析的模块使用的模块。
未知函数是指被使用的函数,它既不是任何已分析模块的本地函数或导出函数,也不是任何库模块导出的函数。未定义函数是指外部使用的函数,它不是任何已分析模块或库模块导出的函数。根据这个概念,本地函数可以是未定义函数,即如果它从某些模块外部使用。所有未知函数也是未定义函数;在用户指南中有一个图说明了这种关系。
可以使用模块属性标记 deprecated
来告知 Xref 关于已弃用的函数,以及(可选)何时计划删除这些函数。以下是一些示例说明了该想法
-deprecated({f,1}).
- 导出的函数f/1
已被弃用。没有说明是否会删除f/1
。-deprecated({f,1,"Use g/1 instead"}).
- 与上面相同,但带有描述性字符串。该字符串目前未被xref
使用,但其他工具可以使用它。-deprecated({f,'_'}).
- 所有导出的函数f/0
、f/1
等都被弃用。-deprecated(module).
- 模块中的所有导出函数都被弃用。等效于-deprecated({'_','_'}).
。-deprecated([{g,1,next_version}]).
- 函数g/1
已被弃用,将在下一个版本中删除。-deprecated([{g,2,next_major_release}]).
- 函数g/2
已被弃用,将在下一个主要版本中删除。-deprecated([{g,3,eventually}]).
- 函数g/3
已被弃用,最终将被删除。-deprecated({'_','_',eventually}).
- 模块中的所有导出函数都被弃用,最终将被删除。
在进行任何分析之前,必须设置模块数据。例如,当所有模块数据已知时,会计算交叉引用和未知函数。需要完整数据的函数(analyze/2,3
、q/2,3
、variables/1,2
)会自动处理数据的设置。在调用任何 add
、replace
、remove
、set_library_path/2,3
或 update/1,2
函数之后,需要(再次)设置模块数据。
设置模块数据的结果是调用图。一个(有向)图由一组顶点和一组(有向)边组成。边表示函数、模块、应用程序或发布版本之间的调用(From,To)。From 被称为调用 To,To 被称为被 From 使用。调用图的顶点是所有模块数据的函数:已分析模块的本地函数和导出函数;使用的 BIF;使用的库模块的导出函数;以及未知函数。编译器添加的函数 module_info/0,1
包含在导出函数中,但仅当从某些模块调用时才包含。边是所有模块数据的函数调用。边的集合意味着如果一个函数在同一行代码中被本地或外部多次使用,则只有一条边。
调用图由 Erlang 术语(集合是列表) 表示,这适用于许多分析。但是对于查看调用链的分析,列表表示太慢了。相反,使用 digraph
模块提供的表示。将调用图的列表表示形式(或其子图)转换为 digraph
表示形式并不是免费的,因此,用于表达下面将描述的查询的语言有一个用于此任务的特殊运算符,以及保存 digraph
表示形式以供后续分析的可能性。
除了调用图之外,还有一个称为内部调用图的图。这是一个调用图(From,To),使得在调用图中存在从 From 到 To 的调用链,并且每个 From 和 To 都是导出函数或未使用的本地函数。顶点与调用图的顶点相同。
模块、应用程序和发布版本之间的调用也是有向图。这些图的顶点和边的类型是(从最特殊到最一般):Fun
用于函数;Mod
用于模块;App
用于应用程序;以及 Rel
用于发布版本。以下段落将描述用于选择和分析图形部分的语言的不同结构,首先从常量开始
- 表达式 ::= 常量
常量 ::= 常量 | 常量
:
类型 | 正则表达式常量 ::= 常量 |
[
常量,
...]
|{
常量,
...}
常量 ::= 调用 | 常量
调用 ::= FunSpec
->
FunSpec |{
MFA,
MFA}
| AtomConst->
AtomConst |{
AtomConst,
AtomConst}
常量 ::= AtomConst | FunSpec | MFA
AtomConst ::= 应用程序 | 模块 | 发布版本
- FunSpec ::= 模块
:
函数/
元数 - MFA ::=
{
模块,
函数,
元数}
RegExpr ::= RegString
:
类型 | RegFunc | RegFunc:
类型- RegFunc ::= RegModule
:
RegFunction/
RegArity - RegModule ::= RegAtom
- RegFunction ::= RegAtom
RegArity ::= RegString | 数字 |
_
|-1
RegAtom ::= RegString | 原子 |
_
- RegString ::= - 正则表达式,如
re
模块中所述,用双引号括起来 - 类型 ::=
Fun
|Mod
|App
|Rel
- 函数 ::= 原子
- 应用程序 ::= 原子
- 模块 ::= 原子
- 发布版本 ::= 原子
元数 ::= 数字 |
-1
- 原子 ::= - 与 Erlang 原子相同 -
- 数字 ::= - 与非负 Erlang 整数相同 -
常量的例子有:kernel
、kernel->stdlib
、[kernel, sasl]
、[pg -> mnesia, {tv, mnesia}] : Mod
。如果 Const
的实例与任何图的任何顶点都不匹配,则会发生错误。如果多个顶点匹配一个未类型的 AtomConst
实例,则选择类型最通用的一个。常量列表被解释为一组常量,它们的类型都相同。常量元组构成一个调用链(可能对应于某个图的实际调用链,也可能不对应)。将类型分配给 Constant
列表或元组等同于将类型分配给每个 Constant
。
正则表达式 用作选择图中某些顶点的方法。由 RegString
和类型组成的 RegExpr
(例如 "xref_.*" : Mod
)被解释为匹配该表达式的模块(或应用程序或版本,具体取决于类型)。类似地,RegFunc
被解释为匹配该表达式的调用图的那些顶点。一个例子是 "xref_.*":"add_.*"/"(2|3)"
,它匹配任何 xref 模块的所有 arity 为 2 或 3 的 add
函数。另一个例子,匹配所有 arity 为 10 或以上的函数:_:_/"[1-9].+"
。这里的 _
是 ".*"
的缩写,即匹配任何内容的正则表达式。
变量的语法很简单
- 表达式 ::= 变量
- 变量 ::= - 与 Erlang 变量相同 -
有两种变量
预定义变量 - 保存模块数据,不能被赋值,只能在查询中使用。
用户变量 - 可以被赋值,通常用于在评估查询时的临时结果,以及用于保存查询结果以便在后续查询中使用。
预定义变量有(标有 (*) 的变量仅在 functions
模式下可用)
E
- 调用图边 (*)。V
- 调用图顶点 (*)。M
- 模块。所有模块:已分析的模块、使用的库模块和未知模块。A
- 应用程序。R
- 版本。ME
- 模块边。所有模块调用。AE
- 应用程序边。所有应用程序调用。RE
- 版本边。所有版本调用。L
- 本地函数 (*)。所有已分析模块的本地函数。X
- 导出函数。所有已分析模块的导出函数以及所有使用的库模块的导出函数。F
- 函数 (*)。B
- 使用的 BIF。如果所有已分析模块的builtins
均为false
,则B
为空。U
- 未知函数。UU
- 未使用函数 (*)。所有已分析模块中未使用的本地和导出函数。XU
- 外部使用的函数。所有模块(包括本地函数)中在某些外部调用中使用的函数。LU
- 本地使用的函数 (*)。所有模块中在某些本地调用中使用的函数。OL
- 具有属性标签on_load
的函数 (*)。LC
- 本地调用 (*)。XC
- 外部调用 (*)。AM
- 已分析的模块。UM
- 未知的模块。LM
- 使用的库模块。UC
- 未解析的调用。在modules
模式下为空。EE
- 跨调用图边 (*)。DF
- 已弃用的函数。所有已弃用的导出函数和所有使用的已弃用 BIF。DF_1
- 已弃用的函数。所有将在下一个版本中删除的已弃用函数。DF_2
- 已弃用的函数。所有将在下一个版本或下一个主要版本中删除的已弃用函数。DF_3
- 已弃用的函数。所有将在下一个版本、下一个主要版本或更高版本中删除的已弃用函数。
以下是关于预定义变量的一些 事实(集合运算符 +
(并集)和 -
(差集)以及类型转换运算符 (
类型)
在下面描述)
F
等于L + X
。V
等于X + L + B + U
,其中X
、L
、B
和U
是两两不相交的(即没有共同元素)。UU
等于V - (XU + LU)
,其中LU
和XU
可能有共同的元素。换句话说V
等于UU + XU + LU
。OL
是F
的子集。E
等于LC + XC
。请注意,LC
和XC
可能有共同的元素,即如果某些函数在同一个函数中被本地和外部使用。U
是XU
的子集。B
是XU
的子集。LU
等于range LC
。XU
等于range XC
。LU
是F
的子集。UU
是F
的子集。range UC
是U
的子集。M
等于AM + LM + UM
,其中AM
、LM
和UM
是两两不相交的。ME
等于(Mod) E
。AE
等于(App) E
。RE
等于(Rel) E
。(Mod) V
是M
的子集。如果所有已分析的模块都有一些本地、导出或未知函数,则等式成立。(App) M
是A
的子集。如果所有应用程序都有一些模块,则等式成立。(Rel) A
是R
的子集。如果所有版本都有一些应用程序,则等式成立。DF_1
是DF_2
的子集。DF_2
是DF_3
的子集。DF_3
是DF
的子集。DF
是X + B
的子集。
一个重要的概念是表达式的转换。类型转换表达式的语法是
- 表达式 ::=
(
类型)
表达式
类型转换运算符的解释取决于指定的类型 Type
、Expression
的类型以及 Expression
解释的元素的结构。如果指定类型等于表达式类型,则不进行转换。否则,转换将一步一步地进行;例如,(Fun) (App) RE
等效于 (Fun) (Mod) (App) RE
。现在假设 Expression
的解释是一组常量(函数、模块、应用程序或版本)。如果指定类型比表达式类型更通用,例如分别为 Mod
和 Fun
,则类型转换表达式的解释是其函数至少在表达式解释中被提及的模块集合。如果指定类型比表达式类型更特殊,例如分别为 Fun
和 Mod
,则解释是模块的所有函数集合(在 modules
模式下,转换是部分的,因为本地函数是未知的)。与应用程序和版本的转换工作方式类似。例如,(App) "xref_.*" : Mod
返回至少包含一个模块的所有应用程序,该模块使得 xref_
是模块名称的前缀。
现在假设 Expression
的解释是一组调用。如果指定类型比表达式类型更通用,例如分别为 Mod
和 Fun
,则类型转换表达式的解释是调用 (M1, M2) 的集合,使得表达式的解释包含从 M1 的某个函数到 M2 的某个函数的调用。如果指定类型比表达式类型更特殊,例如分别为 Fun
和 Mod
,则解释是所有函数调用 (F1, F2) 的集合,使得表达式的解释包含调用 (M1, M2),并且 F1 是 M1 的函数,F2 是 M2 的函数(在 modules
模式下,没有函数调用,因此转换为 Fun
始终产生空集)。同样,与应用程序和版本的转换工作方式类似。
常量和变量的解释是集合,这些集合可用作通过应用集合运算符来形成新集合的基础。语法是
- 表达式 ::= 表达式 二元集合运算符 表达式
二元集合运算符 ::=
+
|*
|-
+
、*
和 -
分别被解释为并集、交集和差集:两个集合的并集包含两个集合的所有元素;两个集合的交集包含两个集合共有的元素;两个集合的差集包含第一个集合中不属于第二个集合的元素。两个集合的元素必须具有相同的结构;例如,函数调用不能与函数组合。但是,如果类型转换运算符可以使元素兼容,则更通用的元素会转换为不太通用的元素类型。例如,M + F
等同于 (Fun) M + F
,而 E - AE
等同于 E - (Fun) AE
。再举一个例子:X * xref : Mod
被解释为模块 xref
导出的函数集合;xref : Mod
被转换为更特殊的类型 X
(即 Fun
),从而产生 xref
的所有函数,并且与 X
(由已分析模块和库模块导出的所有函数)的交集被解释为由某些模块导出的函数并且是 xref
的函数。
还有一元集合运算符
- Expression ::= UnarySetOp Expression
UnarySetOp ::=
domain
|range
|strict
回想一下,调用是一个对 (From, To)。应用于调用集合的 domain
被解释为所有顶点 From 的集合,而 range
被解释为所有顶点 To 的集合。 strict
运算符的解释是从操作数中删除所有形式为 (A, A) 的调用。
限制运算符 的解释是第一个操作数的子集,即调用集合。第二个操作数(顶点集合)被转换为第一个操作数的类型。限制运算符的语法为
- Expression ::= Expression RestrOp Expression
- RestrOp ::=
|
- RestrOp ::=
||
- RestrOp ::=
|||
以下是三个运算符的详细解释:
|
- 来自任何顶点的调用子集。||
- 到任何顶点的调用子集。|||
- 到和来自任何顶点的调用子集。对于所有调用集合CS
和所有顶点集合VS
,CS ||| VS
等同于CS | VS * CS || VS
。
如果两个函数(模块、应用程序、发布版本)彼此(间接)调用,则它们属于同一个强连通分量。 components
运算符的解释是一组调用的强连通分量集合。调用集合的 condensation
是在强连通分量之间的新调用集合,使得如果第一个分量的某些常量调用了第二个分量的某些常量,则两个分量之间就存在一条边。
of
运算符的解释是第二个操作数(一组调用)的调用链,该调用链按照给定的顺序传递第一个操作数(常量元组)的所有顶点。第二个操作数被转换为第一个操作数的类型。例如,of
运算符可用于找出某个函数是否间接调用另一个函数,而调用链则展示了如何进行调用。图形分析运算符的语法如下
- Expression ::= Expression BinaryGraphOp Expression
- Expression ::= UnaryGraphOp Expression
UnaryGraphOp ::=
components
|condensation
- BinaryGraphOp ::=
of
如前所述,图形分析操作是在图形的 digraph
表示上进行的。默认情况下,digraph
表示在需要时创建(并在不再使用时删除),但也可以使用 closure
运算符显式创建
- Expression ::= ClosureOp Expression
- ClosureOp ::=
closure
closure
运算符的解释是操作数的传递闭包。
限制运算符也为闭包定义;closure E | xref : Mod
被解释为来自 xref
模块的直接或间接函数调用,而 E | xref : Mod
的解释是来自 xref
的直接调用集合。如果要在多个图形分析中使用同一个图形,则将该图形的 digraph
表示分配给用户变量可以节省时间,然后确保每个图形分析都对该变量而不是图形的列表表示进行操作。
函数定义所在的行(更准确地说:第一个子句开始的位置)和函数被使用的行在 functions
模式下可用。行号指的是定义函数的文件。这对于通过 -include
和 -include_lib
指令包含的文件也适用,这可能会导致函数表面上在同一行定义。行运算符 用于将行号分配给函数,以及将行号集合分配给函数调用。语法类似于类型转换运算符的语法
- Expression ::=
(
LineOp)
Expression - Expression ::=
(
XLineOp)
Expression LineOp ::=
Lin
|ELin
|LLin
|XLin
- XLineOp ::=
XXL
应用于函数集合的 Lin
运算符的解释是将函数定义所在的行号分配给每个函数。未知函数和库模块的函数被分配数字 0。
应用于函数调用集合的某个 LineOp 运算符的解释是将第一个函数调用第二个函数的行号集合分配给每个调用。并非所有调用都由所有运算符分配行号
Lin
运算符为调用图边定义;LLin
运算符为本地调用定义。XLin
运算符为外部调用定义。ELin
运算符为中间调用图边定义。
Lin
(LLin
、XLin
)运算符分配发出调用(本地调用、外部调用)所在的行。 ELin
运算符为每个调用 (From, To) 分配每个行 L,使得存在一条从 From 到 To 的调用链,该调用链以 L 行的调用开始。
XXL
运算符为应用于函数调用集合的任何 LineOp 运算符的解释定义。结果是将函数调用替换为带有行号的函数调用,也就是说,调用的两个函数中的每一个都被替换为函数和定义该函数的行对。 XXL
运算符的效果可以通过 LineOp 运算符撤消。例如,(Lin) (XXL) (Lin) E
等同于 (Lin) E
。
如果操作数兼容,则为行号表达式定义 +
、-
、*
和 #
运算符。 LineOp 运算符也为模块、应用程序和发布版本定义;操作数隐式转换为函数。同样,类型转换运算符为 LineOp 运算符的解释定义。
计数运算符 的解释是集合的元素数量。该运算符对于闭包未定义。当应用于数字时,+
、-
和 *
运算符被解释为明显的算术运算符。计数运算符的语法为
- Expression ::= CountOp Expression
- CountOp ::=
#
所有二元运算符都是左结合的;例如,A | B || C
等同于 (A | B) || C
。以下是所有运算符的列表,按优先级递增顺序排列
+
,-
*
#
|
,||
,|||
of
(
Type)
closure
,components
,condensation
,domain
,range
,strict
括号用于分组,以使表达式更具可读性或覆盖运算符的默认优先级
- Expression ::=
(
Expression)
查询 是一个非空的语句序列。语句是对用户变量的赋值或表达式。赋值的值是右侧表达式的值。将普通表达式放在查询的最后位置以外的其他任何位置是没有意义的。查询的语法由以下产生式概括
- Query ::= Statement
,
... Statement ::= Assignment | Expression
Assignment ::= Variable
:=
Expression | Variable=
Expression
除非先删除变量,否则不能为该变量分配新值。通过 =
运算符分配的变量在查询结束时删除,而通过 :=
运算符分配的变量只能通过调用 forget
删除。当需要再次设置模块数据时,没有用户变量;如果调用任何需要再次设置模块数据的函数,则会忘记所有用户变量。
另请参阅
摘要
类型
-type add_dir_rsn() :: {file_error, file(), file_error()} | {invalid_filename, term()} | {invalid_options, term()} | {unrecognized_file, file()} | beam_lib:chnk_rsn().
-type add_mod_rsn() :: {file_error, file(), file_error()} | {invalid_filename, term()} | {invalid_options, term()} | {module_clash, {module(), file(), file()}} | {no_debug_info, file()} | beam_lib:chnk_rsn().
-type analysis() :: undefined_function_calls | undefined_functions | locals_not_used | exports_not_used | deprecated_function_calls | {deprecated_function_calls, DeprFlag :: depr_flag()} | deprecated_functions | {deprecated_functions, DeprFlag :: depr_flag()} | {call, FuncSpec :: func_spec()} | {use, FuncSpec :: func_spec()} | {module_call, ModSpec :: mod_spec()} | {module_use, ModSpec :: mod_spec()} | {application_call, AppSpec :: app_spec()} | {application_use, AppSpec :: app_spec()} | {release_call, RelSpec :: rel_spec()} | {release_use, RelSpec :: rel_spec()}.
-type answer() :: false | [constant()] | [(Call :: call()) | (ComponentCall :: {component(), component()})] | [Component :: component()] | non_neg_integer() | [DefineAt :: define_at()] | [CallAt :: {funcall(), LineNumbers :: [non_neg_integer()]}] | [AllLines :: {{define_at(), define_at()}, LineNumbers :: [non_neg_integer()]}].
-type app_spec() :: application() | [application()].
-type application() :: atom().
-type component() :: [constant()].
-type constant() :: xmfa() | module() | application() | release().
-type define_at() :: {xmfa(), LineNumber :: non_neg_integer()}.
-type depr_flag() :: next_version | next_major_release | eventually.
-type directory() :: atom() | file:filename().
-type file() :: file:filename().
-type file_error() :: atom().
-type function_name() :: atom().
-type info() :: {application, Application :: [application()]} | {builtins, boolean()} | {directory, directory()} | {library_path, library_path()} | {mode, mode()} | {no_analyzed_modules, non_neg_integer()} | {no_applications, non_neg_integer()} | {no_calls, {NoResolved :: non_neg_integer(), NoUnresolved :: non_neg_integer()}} | {no_function_calls, {NoLocal :: non_neg_integer(), NoResolvedExternal :: non_neg_integer(), NoUnresolved :: non_neg_integer()}} | {no_functions, {NoLocal :: non_neg_integer(), NoExternal :: non_neg_integer()}} | {no_inter_function_calls, non_neg_integer()} | {no_releases, non_neg_integer()} | {release, Release :: [release()]} | {version, Version :: [non_neg_integer()]}.
-type library() :: atom().
-type library_path() :: path() | code_path.
-type mode() :: functions | modules.
-type path() :: [file()].
-type release() :: atom().
-type string_position() :: pos_integer().
-type variable() :: atom().
-type xarity() :: arity() | -1.
-type xmfa() :: {module(), function_name(), xarity()}.
函数
-spec add_application(XrefServer, Directory) -> {ok, application()} | {error, module(), Reason} when XrefServer :: xref(), Directory :: directory(), Reason :: {application_clash, {application(), directory(), directory()}} | add_dir_rsn().
-spec add_application(XrefServer, Directory, Options) -> {ok, application()} | {error, module(), Reason} when XrefServer :: xref(), Directory :: directory(), Options :: Option | [Option], Option :: {builtins, boolean()} | {name, application()} | {verbose, boolean()} | {warnings, boolean()} | builtins | verbose | warnings, Reason :: {application_clash, {application(), directory(), directory()}} | add_dir_rsn().
将应用程序、应用程序的模块和模块的 模块数据 添加到 Xref 服务器。
这些模块将成为应用程序的成员。默认情况下,使用目录的基本名称(去除版本号)作为应用程序名称,但可以使用 name
选项覆盖此设置。返回应用程序的名称。
如果给定目录有一个名为 ebin
的子目录,则在该目录中搜索模块(BEAM 文件);否则,在给定目录中搜索模块。
-spec add_directory(XrefServer, Directory) -> {ok, Modules} | {error, module(), Reason} when XrefServer :: xref(), Directory :: directory(), Modules :: [module()], Reason :: {application_clash, {application(), directory(), directory()}} | add_dir_rsn().
-spec add_directory(XrefServer, Directory, Options) -> {ok, Modules} | {error, module(), Reason} when XrefServer :: xref(), Directory :: directory(), Options :: Option | [Option], Option :: {builtins, boolean()} | {recurse, boolean()} | {verbose, boolean()} | {warnings, boolean()} | builtins | recurse | verbose | warnings, Modules :: [module()], Reason :: add_dir_rsn().
将给定目录中找到的模块和 模块的数据 添加到 Xref 服务器。
默认情况下不检查子目录,但如果选项 recurse
的值为 true
,则会在所有级别的子目录以及给定目录中搜索模块。返回已添加模块名称的排序列表。
添加的模块将不属于任何应用程序的成员。
-spec add_module(XrefServer, File) -> {ok, module()} | {error, module(), Reason} when XrefServer :: xref(), File :: file:filename(), Reason :: add_mod_rsn().
-spec add_module(XrefServer, File, Options) -> {ok, module()} | {error, module(), Reason} when XrefServer :: xref(), File :: file:filename(), Options :: Option | [Option], Option :: {builtins, boolean()} | {verbose, boolean()} | {warnings, boolean()} | builtins | verbose | warnings, Reason :: add_mod_rsn().
该模块将不属于任何应用程序的成员。返回模块的名称。
如果 Xref 服务器的模式为 functions
,并且 BEAM 文件不包含调试信息,则会返回错误消息 no_debug_info
。
-spec add_release(XrefServer, Directory, Options) -> {ok, release()} | {error, module(), Reason} when XrefServer :: xref(), Directory :: directory(), Options :: Option | [Option], Option :: {builtins, boolean()} | {name, release()} | {verbose, boolean()} | {warnings, boolean()} | builtins | verbose | warnings, Reason :: {application_clash, {application(), directory(), directory()}} | {release_clash, {release(), directory(), directory()}} | add_dir_rsn().
将发布版本、发布版本的应用程序、应用程序的模块以及模块的 模块数据 添加到 Xref 服务器。
这些应用程序将成为发布的成员,而模块将成为应用程序的成员。默认情况下,使用目录的基本名称作为发布名称,但可以使用 name
选项覆盖此设置。返回发布的名称。
如果给定目录有一个名为 lib
的子目录,则该目录中的目录被假定为应用程序目录;否则,给定目录的所有子目录都被假定为应用程序目录。如果存在某个应用程序的多个版本,则选择版本最高的那个。
-spec analyze(XrefServer, Analysis) -> {ok, Answer} | {error, module(), Reason} when XrefServer :: xref(), Analysis :: analysis(), Answer :: [term()], Reason :: analyze_rsn().
-spec analyze(XrefServer, Analysis, Options) -> {ok, Answer} | {error, module(), Reason} when XrefServer :: xref(), Analysis :: analysis(), Options :: Option | [Option], Option :: {verbose, boolean()} | verbose, Answer :: [term()], Reason :: analyze_rsn().
根据所选分析,返回不重复的call/0
或 constant/0
的排序列表。预定义的分析(对所有分析模块进行操作)如下(标有 (*) 的分析仅在模式 functions
下可用):
undefined_function_calls
(*) - 返回对未定义函数的调用列表。undefined_functions
- 返回未定义函数的列表。locals_not_used
(*) - 返回未在本地使用的本地函数列表。exports_not_used
- 返回未在外部使用的导出函数列表。请注意,在modules
模式下,M:behaviour_info/1
永远不会被报告为未使用。deprecated_function_calls
(*) - 返回对已弃用函数的外部调用列表。{deprecated_function_calls, DeprFlag}
(*) - 返回对已弃用函数的外部调用列表。如果DeprFlag
等于next_version
,则返回对将在下一版本中删除的函数的调用。如果DeprFlag
等于next_major_release
,则返回对将在下一主版本中删除的函数以及将在下一版本中删除的函数的调用。最后,如果DeprFlag
等于eventually
,则返回对所有要删除的函数的调用,包括对将在下一版本或下一主版本中删除的函数的调用。deprecated_functions
- 返回外部使用的已弃用函数列表。{deprecated_functions, DeprFlag}
- 返回外部使用的已弃用函数列表。如果DeprFlag
等于next_version
,则返回将在下一版本中删除的函数。如果DeprFlag
等于next_major_release
,则返回将在下一主版本中删除的函数以及将在下一版本中删除的函数。最后,如果DeprFlag
等于eventually
,则返回所有要删除的函数,包括将在下一版本或下一主版本中删除的函数。{call, FuncSpec}
(*) - 返回由给定函数调用的函数列表。{use, FuncSpec}
(*) - 返回使用给定函数的函数列表。{module_call, ModSpec}
- 返回由给定模块调用的模块列表。{module_use, ModSpec}
- 返回使用给定模块的模块列表。{application_call, AppSpec}
- 返回由给定应用程序调用的应用程序列表。{application_use, AppSpec}
- 返回使用给定应用程序的应用程序列表。{release_call, RelSpec}
- 返回由给定发布调用的发布列表。{release_use, RelSpec}
- 返回使用给定发布的发布列表。
-spec d(Directory) -> [DebugInfoResult] | [NoDebugInfoResult] | {error, module(), Reason} when Directory :: directory(), DebugInfoResult :: {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]}, NoDebugInfoResult :: {deprecated, [xmfa()]} | {undefined, [xmfa()]}, Reason :: {file_error, file(), file_error()} | {invalid_filename, term()} | {unrecognized_file, file()} | beam_lib:chnk_rsn().
检查给定目录中的模块是否调用了已弃用的函数、调用了未定义的函数以及是否存在未使用的本地函数。
代码路径用作库路径。
如果找到的某些 BEAM 文件包含调试信息,则检查这些模块并返回元组列表。每个元组的第一个元素是以下之一:
deprecated
,第二个元素是对已弃用函数的排序调用列表;undefined
,第二个元素是对未定义函数的排序调用列表;unused
,第二个元素是未使用的本地函数的排序列表。
如果没有 BEAM 文件包含调试信息,则返回元组列表。每个元组的第一个元素是以下之一:
deprecated
,第二个元素是外部使用的已弃用函数的排序列表;undefined
,第二个元素是未定义函数的排序列表。
-spec forget(XrefServer) -> ok when XrefServer :: xref().
-spec format_error(Error) -> io_lib:chars() when Error :: {error, module(), Reason :: term()}.
给定此模块的任何函数返回的错误,函数 format_error
返回错误的英文描述字符串。
对于文件错误,调用函数 file:format_error/1
。
-spec get_default(XrefServer) -> [{Option, Value}] when XrefServer :: xref(), Option :: builtins | recurse | verbose | warnings, Value :: boolean().
返回所有选项及其默认值的列表。
-spec get_default(XrefServer, Option) -> {ok, Value} | {error, module(), Reason} when XrefServer :: xref(), Option :: builtins | recurse | verbose | warnings, Value :: boolean(), Reason :: {invalid_options, term()}.
返回选项 Option
的默认值。
-spec get_library_path(XrefServer) -> {ok, LibraryPath} when XrefServer :: xref(), LibraryPath :: library_path().
返回库路径。
info/1
函数以某种顺序返回有关Xref 服务器的状态和模块数据的信息,形式为 {Tag, term()
的键值对列表。
info/1
返回带有以下标签的信息(标有 (*) 的标签仅在 functions
模式下可用):
-spec info(XrefServer, Category) -> [{Item, [Info]}] | {error, module(), {no_such_info, Category}} when XrefServer :: xref(), Category :: modules | applications | releases | libraries, Item :: module() | application() | release() | library(), Info :: info().
返回属于类别 Category
的所有项的信息。有关详细信息,请参见info/3
。
-spec info(XrefServer, Category, Items) -> [{Item, [Info]}] | {error, module(), Reason} when XrefServer :: xref(), Category :: modules | applications | releases | libraries, Items :: Item | [Item], Item :: module() | application() | release() | library(), Info :: info(), Reason :: {no_such_application, Item} | {no_such_info, Category} | {no_such_library, Item} | {no_such_module, Item} | {no_such_release, Item}.
info
函数以某种顺序返回关于 Xref 服务器 的状态和模块数据的信息,形式为 {Tag, term()}
的键值对列表。
info/2
和 info/3
返回有关 Xref 服务器的所有或某些分析模块、应用程序、发布或库模块的信息。为每个分析模块返回以下信息:
application
,如果模块不属于任何应用程序,则为空列表;否则,为应用程序名称列表;builtins
,是否在模块的数据中包含对 BIF 的调用;directory
,模块的 BEAM 文件所在的目录;no_calls
(*),调用次数,将不同行中的一个函数调用实例视为单独的调用;no_function_calls
(*),本地调用、已解析的外部调用和未解析调用的次数;no_functions
(*),本地和导出函数的数量;no_inter_function_calls
(*),内部调用图的调用次数;
为每个应用程序返回以下信息:
directory
,模块的 BEAM 文件所在的目录;no_analyzed_modules
,分析模块的数量;no_calls
(*),应用程序模块的调用次数,将不同行中的一个函数调用实例视为单独的调用;no_function_calls
(*),应用程序模块的本地调用、已解析的外部调用和未解析调用的次数;no_functions
(*),应用程序模块的本地和导出函数的数量;no_inter_function_calls
(*),应用程序模块的内部调用图的调用次数;release
,如果应用程序不属于任何发布,则为空列表;否则,为发布名称列表;version
,应用程序的版本,以数字列表表示。例如,目录“kernel-2.6”会产生应用程序名称kernel
和应用程序版本 [2,6];“kernel”会产生名称kernel
和版本 []。
为每个发布返回以下信息:
directory
,发布目录;no_analyzed_modules
,分析模块的数量;no_applications
,应用程序数量;no_calls
(*),发布模块的调用次数,将不同行中的一个函数调用实例视为单独的调用;no_function_calls
(*),发布模块的本地调用、已解析的外部调用和未解析调用的次数;no_functions
(*),发布模块的本地和导出函数的数量;no_inter_function_calls
(*),发布模块的内部调用图的调用次数。
为每个库模块返回以下信息:
directory
,库模块的 BEAM 文件所在的目录。
对于 no_
标签返回的每个调用、函数等等的数量,都有一个查询返回相同的数量。下面列出这些查询的示例。一些查询返回两个或多个 no_
标签数字的总和。mod
(app
,rel
)指的是任何模块(应用程序,发布版本)。
no_analyzed_modules
"# AM"
(info/1)"# (Mod) app:App"
(应用程序)"# (Mod) rel:Rel"
(发布版本)
no_applications
"# A"
(info/1)
no_calls
。已解析和未解析的调用数量的总和"# (XLin) E + # (LLin) E"
(info/1)"T = E | mod:Mod, # (LLin) T + # (XLin) T"
(模块)"T = E | app:App, # (LLin) T + # (XLin) T"
(应用程序)"T = E | rel:Rel, # (LLin) T + # (XLin) T"
(发布版本)
no_functions
。库模块中的函数和module_info/0,1
函数不被info
计数。假设已经评估了"Extra := _:module_info/\"(0|1)\" + LM"
,则局部函数和导出函数的总和为"# (F - Extra)"
(info/1)"# (F * mod:Mod - Extra)"
(模块)"# (F * app:App - Extra)"
(应用程序)"# (F * rel:Rel - Extra)"
(发布版本)
no_function_calls
。本地调用、已解析的外部调用和未解析的调用数量的总和"# LC + # XC"
(info/1)"# LC | mod:Mod + # XC | mod:Mod"
(模块)"# LC | app:App + # XC | app:App"
(应用程序)"# LC | rel:Rel + # XC | mod:Rel"
(发布版本)
no_inter_function_calls
"# EE"
(info/1)"# EE | mod:Mod"
(模块)"# EE | app:App"
(应用程序)"# EE | rel:Rel"
(发布版本)
no_releases
"# R"
(info/1)
-spec m(FileOrModule) -> [DebugInfoResult] | [NoDebugInfoResult] | {error, module(), Reason} when FileOrModule :: file:filename() | module(), DebugInfoResult :: {deprecated, [funcall()]} | {undefined, [funcall()]} | {unused, [mfa()]}, NoDebugInfoResult :: {deprecated, [xmfa()]} | {undefined, [xmfa()]}, Reason :: {cover_compiled, Module :: module()} | {file_error, file(), file_error()} | {interpreted, Module :: module()} | {invalid_filename, term()} | {no_such_module, Module :: module()} | beam_lib:chnk_rsn().
检查给定的 BEAM 文件(带有或不带有 .beam
扩展名)或通过调用 code:which(Module)
找到的文件是否调用了已弃用的函数、调用了未定义的函数以及是否存在未使用的本地函数。
代码路径用作库路径。
如果 BEAM 文件包含 调试信息,则返回一个元组列表。每个元组的第一个元素是以下之一
deprecated
,第二个元素是对已弃用函数的排序调用列表;undefined
,第二个元素是对未定义函数的排序调用列表;unused
,第二个元素是未使用的本地函数的排序列表。
如果 BEAM 文件不包含调试信息,则返回一个元组列表。每个元组的第一个元素是以下之一
deprecated
,第二个元素是外部使用的已弃用函数的排序列表;undefined
,第二个元素是未定义函数的排序列表。
-spec q(XrefServer, Query, Options) -> {ok, Answer} | {error, module(), Reason} when XrefServer :: xref(), Query :: string() | atom(), Options :: Option | [Option], Option :: {verbose, boolean()} | verbose, Answer :: answer(), Reason :: q_rsn().
在 Xref 服务器 的上下文中评估查询,并返回最后一个语句的值。
值的语法取决于表达式
- 一组调用由一个排序后的不重复的
call/0
列表表示。 - 一组常量由一个排序后的不重复的
constant/0
列表表示。 - 一组强连通分量是一个排序后的不重复的
Component
列表。 - 一组强连通分量之间的调用是一个排序后的不重复的
ComponentCall
列表。 - 调用链由一个
constant/0
列表表示。该列表包含每个调用的 From 顶点和最后一个调用的 To 顶点。 - 如果在给定的常量之间找不到调用链,则
of
运算符返回false
。 closure
运算符(digraph
表示)的值由原子'closure()'
表示。- 一组带行号的函数由一个排序后的不重复的
DefineAt
列表表示。 - 一组带行号的函数调用由一个排序后的不重复的
CallAt
列表表示。 - 一组带行号的函数和函数调用由一个排序后的不重复的
AllLines
列表表示。
对于 CallAt
和 AllLines
,对于任何列表元素,LineNumbers
都不是一个空列表;此类元素已被删除。component
的常量和 LineNumbers
的整数已排序且不重复。
-spec remove_application(XrefServer, Applications) -> ok | {error, module(), Reason} when XrefServer :: xref(), Applications :: application() | [application()], Reason :: {no_such_application, application()}.
-spec replace_application(XrefServer, Application, Directory) -> {ok, Application} | {error, module(), Reason} when XrefServer :: xref(), Application :: application(), Directory :: directory(), Reason :: {no_such_application, Application} | add_dir_rsn().
等同于 replace_application(XrefServer, Application, Directory, [])
。
-spec replace_application(XrefServer, Application, Directory, Options) -> {ok, Application} | {error, module(), Reason} when XrefServer :: xref(), Application :: application(), Directory :: directory(), Options :: Option | [Option], Option :: {builtins, boolean()} | {verbose, boolean()} | {warnings, boolean()} | builtins | verbose | warnings, Reason :: {application_clash, {application(), directory(), directory()}} | {no_such_application, Application} | add_dir_rsn().
将应用程序的模块替换为从应用程序目录读取的其他模块。
保留应用程序的发布版本成员资格。请注意,保留应用程序的名称;不使用给定目录的名称。
-spec replace_module(XrefServer, Module, File, Options) -> {ok, Module} | {error, module(), Reason} when XrefServer :: xref(), Module :: module(), File :: file(), Options :: Option | [Option], Option :: {verbose, boolean()} | {warnings, boolean()} | verbose | warnings, Reason :: {module_mismatch, Module, ReadModule :: module()} | {no_such_module, Module} | add_mod_rsn().
保留模块的应用程序成员资格,以及模块的 builtins
选项的值。如果读取的模块名称与给定的模块不同,则会返回错误。
update
函数是更新重新编译模块的模块数据的替代方法。
-spec set_default(XrefServer, OptionValues) -> ok | {error, module(), Reason} when XrefServer :: xref(), OptionValues :: OptionValue | [OptionValue], OptionValue :: {Option, Value}, Option :: builtins | recurse | verbose | warnings, Value :: boolean(), Reason :: {invalid_options, term()}.
为 OptionValues
给出的多个选项设置默认值。
有关选项的名称及其允许的值,请参阅 set_default/3
。
-spec set_default(XrefServer, Option, Value) -> {ok, OldValue} | {error, module(), Reason} when XrefServer :: xref(), Option :: builtins | recurse | verbose | warnings, Value :: boolean(), OldValue :: boolean(), Reason :: {invalid_options, term()}.
设置一个或多个选项的默认值。
可以通过这种方式设置的选项有
builtins
,初始默认值为false
;recurse
,初始默认值为false
;verbose
,初始默认值为false
;warnings
,初始默认值为true
。
在创建 Xref 服务器时设置初始默认值。
-spec set_library_path(XrefServer, LibraryPath) -> ok | {error, module(), Reason} when XrefServer :: xref(), LibraryPath :: library_path(), Reason :: {file_error, file(), file_error()} | {invalid_options, term()} | {invalid_path, term()}.
-spec set_library_path(XrefServer, LibraryPath, Options) -> ok | {error, module(), Reason} when XrefServer :: xref(), LibraryPath :: library_path(), Options :: Option | [Option], Option :: {verbose, boolean()} | verbose, Reason :: {invalid_options, term()} | {invalid_path, term()}.
设置库路径。
如果给定的路径是目录列表,则通过按照给定顺序遍历目录时选择遇到的第一个模块来确定 库模块的集合,对于那些在多个目录中出现的模块。默认情况下,库路径是一个空列表。
函数 m/1
和 d/1
使用库路径 code_path
,但也可以显式设置。但是,请注意,在设置模块数据时,对于每个使用的 库模块,都会遍历一次代码路径。另一方面,如果只有少数模块被使用但未被分析,则使用 code_path
可能比将库路径设置为 code:get_path/0
更快。
如果将库路径设置为 code_path
,则不确定库模块的集合,并且 info
函数将返回空的库模块列表。
-spec start(NameOrOptions) -> {ok, pid()} | {error, {already_started, pid()}} when NameOrOptions :: Name | Options, Name :: atom(), Options :: Option | [Option], Option :: {xref_mode, mode()} | term().
创建 Xref 服务器。
可以选择给进程命名。默认的 模式 是 functions
。Xref 未识别的选项会传递给 gen_server:start/4
。
-spec start(Name, Options) -> {ok, pid()} | {error, {already_started, pid()}} when Name :: atom(), Options :: Option | [Option], Option :: {xref_mode, mode()} | term().
创建具有给定名称的 Xref 服务器。
默认的 模式 是 functions
。Xref 未识别的选项会传递给 gen_server:start/4
。
-spec stop(XrefServer) -> stopped when XrefServer :: xref().
停止 Xref 服务器。
-spec update(XrefServer, Options) -> {ok, Modules} | {error, module(), Reason} when XrefServer :: xref(), Options :: Option | [Option], Option :: {verbose, boolean()} | {warnings, boolean()} | verbose | warnings, Modules :: [module()], Reason :: {module_mismatch, module(), ReadModule :: module()} | add_mod_rsn().
替换自上次被 add
函数或 update
读取以来,所有 已分析模块的 BEAM 文件被修改过的模块数据。
保留模块的应用程序成员资格,以及 builtins
选项的值。返回已替换模块名称的排序列表。
-spec variables(XrefServer, Options) -> {ok, [VariableInfo]} when XrefServer :: xref(), Options :: Option | [Option], Option :: predefined | user | {verbose, boolean()} | verbose, VariableInfo :: {predefined, [variable()]} | {user, [variable()]}.
返回 Xref 服务器的变量名称的排序列表。
默认情况下,仅返回 用户变量。