news 2026/9/23 20:33:54

Diem Move Prover Docgen 输出格式解析:从 TestViz 模块看不同可见性函数的文档生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Diem Move Prover Docgen 输出格式解析:从 TestViz 模块看不同可见性函数的文档生成

Diem Move Prover Docgen 输出格式解析:从 TestViz 模块看不同可见性函数的文档生成

【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem

本指南以 Diem 仓库中 Move Prover Docgen 的基线测试输出 different_visbilities.spec_inline.md 为核心样本,系统拆解文档生成器(Docgen)为 Move 模块生成的 Markdown 文档结构、锚点与目录机制、函数可见性对文档内容的影响,以及对应的命令行与配置选项。读完本文,你将能读懂并复现 Docgen 生成的模块文档,理解publicpublic(script)、私有函数在文档中的呈现差异,并掌握如何通过源码与测试基线验证文档生成的正确性。

关联文档的定位:一份 Docgen 基线测试输出

需要先说明本文所依据的样本文档是什么。different_visbilities.spec_inline.md并不是一份手写教程,而是 Diem 仓库中Docgen 测试套件的期望输出基线(baseline):测试工具先对 Move 源文件运行文档生成器,再将生成结果与这份基线文件逐字比对,从而验证生成器行为没有回归。

相关文件均位于 language/move-prover/docgen 目录下:

  • 测试输入:different_visbilities.move —— 定义了包含三种可见性函数的TestViz模块;
  • 期望输出(本文主体):different_visbilities.spec_inline.md ——specs_inlined=true、折叠实现区(<details>)模式;
  • 同源另两个变体:different_visbilities.spec_separate.md 与 different_visbilities.spec_inline_no_fold.md;
  • 测试驱动:testsuite.rs;
  • 生成器实现:docgen.rs;
  • 用户手册:doc/user/docgen.md。

从测试套件 testsuite.rs 的FLAGS可以看到,Docgen 内嵌于 Move Prover 中,通过--docgen开关启用,并依赖--dependency=../../move-stdlib/modules--dependency=../../diem-framework/modules提供标准库与框架依赖。

源头模块:TestViz 与三种函数可见性

生成这份文档的 Move 源文件很短,却刻意覆盖了 Move 语言函数可见性的三种典型形态,见 different_visbilities.move:

address 0x2 { module TestViz { /// This is a public function public fun this_is_a_public_fun() { } // /// This is a public friend function // public(friend) fun this_is_a_public_friend_fun() {} /// This is a public script function public(script) fun this_is_a_public_script_fun() {} /// This is a private function fun this_is_a_private_fun() {} } }

三个被测试的函数分别是:

函数可见性关键字文档注释
this_is_a_public_funpublic fun/// This is a public function
this_is_a_public_script_funpublic(script) fun/// This is a public script function
this_is_a_private_funfun(模块私有)/// This is a private function

值得注意,源文件中被注释掉的public(friend) fun this_is_a_public_friend_fun是测试作者留下的痕迹,说明该用例意在对照不同可见性在文档中的渲染;由于被注释,它不会出现在生成文档中。三处///注释正是 Docgen 读取并写入文档正文的文档注释(详见后文"文档注释书写规范"一节)。

生成文档的结构逐段解读

下面以基线 different_visbilities.spec_inline.md 为准,逐段还原其完整内容并解释每一部分的生成机制。

模块锚点与一级标题

文档以 HTML 锚点与一级标题开头:

<a name="0x2_TestViz"></a> # Module `0x2::TestViz`

锚点由 docgen.rs 的make_label_for_module生成:取模块全名(含地址)0x2::TestViz,将::替换为_,得到0x2_TestViz。标题级别则由 section_header 控制:section_level_start(默认 1)加上当前嵌套深度(模块层为 0),因此模块标题是单个#,而后续函数小节为##

自动生成的模块目录

紧随标题之后是 Docgen 自动生成的目录:

- [Function `this_is_a_public_fun`](#0x2_TestViz_this_is_a_public_fun) - [Function `this_is_a_public_script_fun`](#0x2_TestViz_this_is_a_public_script_fun) - [Function `this_is_a_private_fun`](#0x2_TestViz_this_is_a_private_fun)

目录条目由 gen_toc 生成,每个条目的锚点格式为{模块标签}_{条目名},即0x2_TestViz_this_is_a_public_fun等(见label_for_module_item的实现,docgen.rs)。条目的可见深度由toc_depth(默认 3)控制,函数按源文件中的出现顺序(按位置排序)依次列出。

模块使用信息代码块

目录之后是一个空代码块:

<pre><code></code></pre>

这并非噪声,而是"使用信息"区域:Docgen 会枚举当前模块在字节码层面使用到的其他模块并输出use <模块>;语句(见 docgen.rs)。TestViz没有任何use声明,因此该代码块为空。如果一个模块依赖了其他模块,这里就会列出对应的use行。

函数小节:签名、文档注释与折叠的实现

每个函数生成一个##小节目录,以第一个函数为例,完整结构如下:

<a name="0x2_TestViz_this_is_a_public_fun"></a> ## Function `this_is_a_public_fun` This is a public function <pre><code><b>public</b> <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_fun">this_is_a_public_fun</a>() </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b> <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_fun">this_is_a_public_fun</a>() { } </code></pre> </details>

组成要素包括:

  1. 锚点<a name="0x2_TestViz_this_is_a_public_fun"></a>,供目录与交叉引用跳转;
  2. 小节标题## Function \this_is_a_public_fun``;
  3. 文档注释正文This is a public function,直接来自源文件中的///注释;
  4. 函数签名代码块:由function_header_display(docgen.rs)生成,包含可见性关键字、类型参数、参数列表与返回类型;
  5. 折叠的实现代码块<details><summary>Implementation</summary>...</details>包裹原始实现源码,由begin_collapsed/end_collapsed(docgen.rs)输出。

三种可见性在文档签名中的呈现

三个函数的签名代码块完整继承如下,正是这份基线文档的核心内容:

公开函数(public fun):

<pre><code><b>public</b> <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_fun">this_is_a_public_fun</a>() </code></pre> <pre><code><b>public</b> <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_fun">this_is_a_public_fun</a>() { } </code></pre>

公开脚本函数(public(script) fun):

<pre><code><b>public</b>(<b>script</b>) <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_script_fun">this_is_a_public_script_fun</a>() </code></pre> <pre><code><b>public</b>(<b>script</b>) <b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_public_script_fun">this_is_a_public_script_fun</a>() {} </code></pre>

私有函数(fun,无修饰符):

<pre><code><b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_private_fun">this_is_a_private_fun</a>() </code></pre> <pre><code><b>fun</b> <a href="different_visbilities.md#0x2_TestViz_this_is_a_private_fun">this_is_a_private_fun</a>() {} </code></pre>

可见性字符串由func_env.visibility_str()提供(在function_header_display中拼接到fun之前),因此publicpublic(script)会原样出现在签名中,私有函数则没有任何前缀。<b>标签是 Docgen 的代码装饰(关键词加粗)结果,<a href="different_visbilities.md#...">则是自动生成的交叉引用——指向该模块自身输出文件different_visbilities.md中的对应锚点(详见下文"交叉引用"一节)。

可见性过滤:私有函数何时进入文档

值得注意的是,私有函数this_is_a_private_fun在默认情况下不一定会出现在生成的文档中。Docgen 通过 gen_module 中的过滤器控制:

let funs = module_env .get_functions() .filter(|f| self.options.include_private_fun || f.is_exposed()) .sorted_by(|a, b| Ord::cmp(&a.get_loc(), &b.get_loc())) .collect_vec();

即:只有include_private_fun为真、或函数本身是暴露(公开)的,才会进入文档。这正是本测试用例专门安排一个私有函数的原因——它用于验证"私有函数也能被文档化"这条路径。

对应到用户手册 doc/user/docgen.md 中的命令行开关:

开关含义用户手册标注的默认值
--doc-include-private=true\|false是否包含私有函数false

但需要提醒:代码层面DocgenOptionsDefault实现(docgen.rs)中include_private_fun: true,而测试套件也显式设置了options.docgen.include_private_fun = true(testsuite.rs)。文档与代码默认值存在差异,因此实际使用时建议显式传参,不要依赖默认行为。

交叉引用与代码装饰机制

基线文档中每个标识符都被加工过,这正是 Docgen 区别于普通 Markdown 渲染器的关键能力,实现在 docgen.rs:

  • 关键字加粗publicfunscript等 Move 关键字(含WEAK_KEYWORDS中的 spec 语言关键字)被包裹为<b>...</b>
  • 标识符超链接resolve_to_label采用启发式解析——优先尝试0xN::Module::item形式的全限定名,也支持Self::itemModule::item与裸名;裸名只有后跟(<(函数调用/泛型实例化)时才解析为函数引用,以避免把字段名误链为函数;
  • HTML 转义<>分别转义为&lt;&gt;,保证签名在<pre><code>块内正确显示;
  • 跨文件引用ref_for_module(docgen.rs)将引用渲染为目标文件#锚点形式。对 TestViz 而言,其输出文件由compute_output_file计算——源文件different_visbilities.move的扩展名替换为.md(docgen.rs),于是所有引用都指向different_visbilities.md#0x2_TestViz_...

三种基线变体:inline / separate / no-fold

同一个测试输入在 testsuite.rs 中会以三组不同选项各生成一份基线:

基线文件specs_inlinedcollapsed_sections差异点
different_visbilities.spec_inline.mdtruetrue实现区用<details>/<summary>折叠
different_visbilities.spec_separate.mdfalsetruespec 独立成节;因本模块无 spec,输出与 inline 几乎一致
different_visbilities.spec_inline_no_fold.mdtruefalse折叠关闭,实现区标题渲染为##### Implementation

对比三个文件可验证collapsed_sections的具体行为:开启时begin_collapsed输出<details><summary>Implementation</summary>(docgen.rs),关闭时则退化为硬编码的##### Implementation五级标题。由于TestViz没有任何spec块,specs_inlined的差异在这里没有体现——若模块带 spec,inline 模式会把规范内联到对应声明的小节下,separate 模式则汇总到文档末尾独立的 Specification 章节(见 gen_spec_section)。

运行 Docgen 生成此类文档

若要在自己的 Move 源码上复现同样的文档,按用户手册 doc/user/docgen.md 中的方式调用:

cargo run -p move-prover -- --docgen <flags> .. <sources>

常用参数:

  • -d=<path>:Move 依赖的搜索路径(编译用);
  • --doc-path=<path>:已生成文档的搜索路径(用于交叉引用);
  • --doc-spec-inline=true|false:spec 是内联到声明处(true,默认)还是汇总到文末独立章节(false);
  • --doc-include-impl=true|false:是否包含函数实现体,默认 true;
  • --doc-include-private=true|false:是否包含私有函数,默认 false(与源码Default存在差异,建议显式指定);
  • --output=<path>:生成文档的输出路径;
  • 完整选项可通过cargo run -p move-prover -- --help查看。

若想直接参与 Docgen 的验证,可运行 Docgen 目录下的测试套件(datatest_stable会扫描 tests/sources 下所有*.move*_template.md),并用verify_or_update_baseline机制比对或更新基线(testsuite.rs)。注意:仓库为只读环境,这里仅说明查看与验证方式,不建议在仓库内直接改动基线。

文档注释书写规范与 Spec 块归属

最后,若希望自己写的模块也能生成类似 TestViz 这样结构清晰的文档,需要遵循 Docgen 对注释与 spec 块的约定(详见 doc/user/docgen.md):

  • 文档注释:以////** ... */开头,必须置于被注释项之前;可作用于模块、结构体、结构体字段、函数、spec 块及 spec 块成员;连续多段注释会合并为一个文档块;
  • 注释内 Markdown:支持任意 Markdown(建议兼容 GitHub 风格),也可使用#小节标题,标题会自动降级嵌入当前上下文层级之下;
  • 代码引用:注释中的foo不会解析为当前模块的函数,除非写成foo()Self::foo
  • Spec 块归属:无明确目标的 schema 与spec module块会归属于其之前最近的函数/结构体 spec 块,否则归属于模块;可在函数 spec 之后放一个空spec module {}强制将后续 spec 归属回模块级,例如:
module M { fun f(): T { ... } spec f { aborts_if f_aborts(); ensures result == f_result(); } // 以下内容跟随函数 f 的文档 spec fun f_aborts() { .. } spec module {} // 以下内容跟随模块文档 spec fun f_result(): T { .. } }

小结

通过 different_visbilities.spec_inline.md 这一基线样本,可以看到 Diem Move Prover Docgen 的完整输出契约:模块锚点与标题、自动目录、使用信息、函数签名与文档注释、折叠实现、关键字加粗与交叉引用。结合 docgen.rs 的实现与 testsuite.rs 的三种选项组合,可以准确推断任何模块文档的生成规则——包括publicpublic(script)与私有函数在文档中的不同呈现,以及--doc-include-private--doc-spec-inlinecollapsed_sections等选项如何改变最终输出。对于维护 Move 生态工具链或需要为 Move 模块生成规范 API 文档的开发者,这份基线与其配套源码是理解 Docgen 行为最直接的参考实现。

【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 20:31:32

5G下行数据传输流程图形化:从协议栈到时序图的绘制指南

简介&#xff1a;一份以图形化方式讲解5G NR下行数据传输流程的PDF资料&#xff0c;源自“5G NR in BULLETS”系列&#xff0c;面向5G技术入门者、网络工程师及通信专业学生&#xff0c;帮助读者从协议栈视角看清数据包从应用层到物理层的完整旅程。资源以CU-DU分离架构下的用户…

作者头像 李华
网站建设 2026/9/23 20:25:40

WorkBuddy 智能体实战:从零搭建每日自动化工作流

1. 为什么我最终把每日重复工作交给了 WorkBuddy每天早上九点坐到工位&#xff0c;打开电脑的第一件事不是写代码&#xff0c;而是打开七八个网页挨个签到、把昨天的订单数据从三个平台导出来合并、再手动整理成日报发到群里。这套动作我做了快两年&#xff0c;熟练到闭着眼睛都…

作者头像 李华
网站建设 2026/9/23 20:24:33

FY-4A卫星云图识别实战:HDF5数据处理与轻量U-Net云分类

简介&#xff1a;本资源是一份面向高校计算机、遥感或人工智能方向本科生的课程设计实践项目&#xff0c;聚焦卫星云层图像的理解与识别任务&#xff0c;提供从传统图像处理到深度学习建模的双路径解决方案。资源共148个文件&#xff0c;包含70个Python源码&#xff08;含U-Net…

作者头像 李华