Rust 编译器错误 E0758 深度解析:未终止块注释(含 Doc 注释)的成因与修复
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
Rust 编译器错误码E0758对应「A multi-line (doc-)comment is unterminated」,即多行块注释(或块 doc 注释)缺少结束定界符*/而一直延伸到文件末尾。本篇基于 compiler/rustc_error_codes/src/error_codes/E0758.md 官方错误码文档展开,并结合 rustc 词法分析器源码,说明该错误从触发、诊断生成到修复的完整链路,读完后可独立定位并修复所有「未终止块注释 / 未终止块 doc 注释」编译错误。
一、错误现象:E0758 的两种典型触发写法
官方错误码文档给出的错误示例如下:
/* I am not terminated!/*! I am not terminated!两个例子分别对应普通块注释/* ... */和块 doc 注释/*! ... */:它们都以/*或/*!开始,但直到文件结束都没有出现*/,编译器便报出 E0758。
修复方式是在注释末尾补上结束符:
/* I am terminated! */ /*! I am also terminated! */需要注意三个要点:
- 块注释与行注释
//是两套独立的词法规则,//结束的行内文字不能用来关闭块注释; - 块注释支持嵌套,每个
*/只关闭最近一层未关闭的/*,嵌套了几层就要对应数量的*/; /*!只是块注释的 doc 变体,语法上与/*完全一致,同样要求*/收尾。
二、源码链路:E0758 是如何被编译器产生的
从源码结构看,E0758 的生成横跨两个 crate:rustc_lexer负责把字节流切成 token,rustc_parse的 Lexer 负责「烹饪」token 并发出诊断。
2.1 词法层:terminated标志的携带
底层词法器rustc_lexer(见 compiler/rustc_lexer/src/lib.rs)在扫描/*时不会立即报错,而是把「是否找到配对的*/」记录在 token 上,其块注释 token 形态为BlockComment { doc_style, terminated }:
doc_style:区分/*(普通)与/*!(inner doc)两种风格;terminated:扫描到文件末尾仍未闭合时为false。
2.2 解析层:检查terminated并触发诊断
rustc_parse的 Lexer 在取下一个 token 的循环中处理该 token(compiler/rustc_parse/src/lexer/mod.rs#L203-L206):
rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { if !terminated { self.report_unterminated_block_comment(start, doc_style); } // ... 普通注释直接跳过,doc 注释继续 cook }可见该错误是在词法阶段(而非语法/类型检查阶段)抛出的,这也是它属于「lexical error」、一旦出现即无法继续解析的原因。
2.3 诊断生成:report_unterminated_block_comment
真正的诊断逻辑在 compiler/rustc_parse/src/lexer/mod.rs#L1010-L1054:
fn report_unterminated_block_comment(&self, start: BytePos, doc_style: Option<DocStyle>) { let msg = match doc_style { Some(_) => "unterminated block doc-comment", None => "unterminated block comment", }; let last_bpos = self.pos; let mut err = self.dcx().struct_span_fatal(self.mk_sp(start, last_bpos), msg); err.code(E0758); // 随后扫描注释内容,配对 / * 与 * / 寻找嵌套注释…… }从中可以确认三条实现事实:
报错文案随注释风格变化:
/*!未闭合时报unterminated block doc-comment,/*未闭合时报unterminated block comment,两者共用 E0758 这一错误码;该错误是 fatal(致命错误):源码使用
struct_span_fatal构造诊断,意味着词法阶段直接中止,编译器不会继续对同文件做后续解析——这解释了为什么修复 E0758 之前往往看不到其他错误;嵌套注释启发式提示:报错后编译器会把注释全文逐字符扫一遍,用栈式配对寻找
/*开与*/闭(L1018-L1033)。若发现其中存在一个已完整闭合的嵌套注释,会额外标注两段 span(L1035-L1050):"...as last nested comment starts here, maybe you want to close this instead?""...and last nested comment terminates here."
这正是针对最常见的误用场景:外层注释未闭合时,内部某处成对出现的
/* ... */其实是嵌套注释。由于*/永远只关闭最近一层未关闭的注释,用户常误以为「加了一个*/就能关闭整个块」。诊断通过指出「最后一层嵌套注释的起止位置」,引导开发者理解注释的嵌套配对关系,进而补上缺失的外层*/。
三、嵌套规则示例:为什么一个*/可能不够
结合 2.3 节的栈式配对逻辑,看一个典型踩坑写法:
/* 外层注释开始 /* 内层嵌套注释 */ 外层仍未闭合此处*/关闭的是内层,外层仍然未终止,编译器报 E0758,并按 2.3 节的方式标注内层注释的起止。正确写法是逐层闭合:
/* 外层注释开始 /* 内层嵌套注释 */ 外层仍未闭合 */这与文档给出的最小修复示例/* I am terminated! */是同一规则的单层特例:单层注释只需一个*/。
四、与相邻「未终止」错误的区分
rustc_parse的词法器对其它未终止字面量有各自的诊断(同一文件 compiler/rustc_parse/src/lexer/mod.rs),排查时应注意区分,避免混淆:
| 报错文案 | 场景 | 出处 |
|---|---|---|
unterminated block comment/unterminated block doc-comment | /*、/*!未闭合 | report_unterminated_block_comment |
unterminated double quote string | "..."字符串未闭合 | compiler/rustc_parse/src/lexer/mod.rs#L812 |
unterminated character literal | '...'字符字面量未闭合 | compiler/rustc_parse/src/lexer/mod.rs#L779 |
unterminated raw string | r#"..."#原始字符串缺少正确数量的#终止符 | report_unterminated_raw_string |
其中unterminated raw string的诊断还会给出修复建议:指出应补写的#数量,并建议「consider terminating the string here」(见 compiler/rustc_parse/src/lexer/mod.rs#L1000-L1004)。而 E0758 没有自动修复建议,只提示补*/。
另外注意一个易混点:字符字面量'未闭合(如'a)与生命周期写法在词法上相邻,源码中存在专门的启发式来区分二者,避免把「未终止字符字面量」误报为其它错误(见 compiler/rustc_lexer/src/lib.rs 中关于 unquoted/unterminated 情况的处理及 compiler/rustc_parse/src/lexer/mod.rs#L180-L183 的last_lifetime推断逻辑)。
五、实战排查清单
遇到 E0758 时,建议按以下顺序处理:
- 看错误 span 起点:span 从注释开头的
/*延伸到文件末尾,起点即未闭合注释的位置; - 数嵌套层数:若编译器给出「last nested comment starts here」标注,说明内部存在已闭合嵌套注释,检查是否漏掉了外层对应的
*/; - 检查 doc 注释变体:若是
/*!开头,确认是unterminated block doc-comment文案,这类注释位于模块/项内部作为 inner doc,忘写*/同样触发 fatal 的 E0758; - 注意 fatal 特性:由于
struct_span_fatal会中止词法分析,E0758 存在时同文件的其它错误可能被「掩盖」,修复注释后重编译往往还会暴露新的错误,属正常现象; - 编辑器辅助:主流 Rust IDE 的括号/注释高亮可以直观看出
/* ... */配对情况,但最终以编译器词法分析为准。
六、关键源码索引
| 内容 | 路径 |
|---|---|
| E0758 错误码官方文档(错误示例与修复示例) | compiler/rustc_error_codes/src/error_codes/E0758.md |
块注释 token 的terminated检查入口 | compiler/rustc_parse/src/lexer/mod.rs#L203-L206 |
诊断生成(文案、struct_span_fatal、err.code(E0758)、嵌套注释启发式标注) | compiler/rustc_parse/src/lexer/mod.rs#L1010-L1054 |
| 底层词法器(注释/字符串 token 划分) | compiler/rustc_lexer/src/lib.rs |
一句话总结:E0758 的根源只有一个——/*//*!开启的块注释缺少与之配对的*/(嵌套时按「一层一个」补齐)。它是词法阶段的 fatal 错误,由report_unterminated_block_comment统一报告,并借助嵌套注释标注帮助开发者理解「*/只关闭最内层注释」这一核心配对规则。
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考