Go语言import声明不同位置多行注释的编译差异原因
Go导入声明中注释位置的语法规则差异
规则基础
Go语言规范中导入声明的语法定义如下:
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) . ImportSpec = [ "." | PackageName ] ImportPath . ImportPath = string_lit .
三个测试用例的编译表现存在明显差异:
- 可正常编译:
import /* */f "fmt"
- 编译失败:
import /* */f/* */"fmt"
- 可正常编译:
import /* */f /* */ "fmt"
核心原因
这个差异本质是Go词法阶段的自动分号插入规则和Token分隔规则共同作用的结果,和注释本身的语义没有关系:
- 先理清楚两个底层规则
- 注释在词法分析阶段会被整体跳过:只有当注释两侧本身有显式空白(空格、换行、制表符)时,注释才会被当作两个Token之间的分隔;如果注释直接贴在两边的Token上、没有其他空白,注释会被直接删掉,相当于不存在,不会起到分隔作用。
- Go为了减少代码里的显式分号书写,设计了自动分号插入机制:当词法分析器读到一个属于「触发分号插入集合」的Token,而且这个Token后面没有显式空白、下一个有效Token本身可以作为新语句的开头时,就会自动在两个Token之间插入一个分号当分隔。会触发分号插入的Token只有这几类:标识符、整数/浮点/字符串/符文等基础字面量、
break/continue/fallthrough/return四个流程控制关键字、++/--/)/]/}五个操作符或分隔符。
- 对应到三个测试用例的表现就很好理解了
import关键字和后续包名f之间插注释不影响编译:import是普通的声明关键字,根本不在分号自动插入的触发列表里。词法分析器按最长匹配规则切出import这个关键字之后,哪怕后面直接跟注释、没有空格,也不会乱插分号,跳过注释读到的f自然会被识别成当前导入语句的包名。- 包名
f和导入路径"fmt"之间直接贴注释、无其他空白时编译失败:f是标识符,属于会触发分号插入的类型。这时候紧贴在f后面的注释会被零宽移除,相当于f直接和后面的"fmt"字符串字面量挨在一起;而字符串字面量本身是可以作为新语句开头的,词法分析器就会自动在f和"fmt"之间插入一个分号,把本来应该是「包名+导入路径」的一个ImportSpec拆成了f;和"fmt"两个独立片段——前者只有包名没有导入路径,后者只有字符串没有对应的语法位置,完全不符合导入声明的语法规则,自然编译失败。 - 包名
f和注释之间存在空格时可以正常编译:f后面的显式空格相当于告诉词法分析器,f所在的语法单元还没结束,不会触发自动分号插入,跳过中间的注释之后,读到的"fmt"就会被正常识别为和f配对的导入路径,完全符合语法要求。
内容的提问来源于stack exchange,提问作者iBug
相关产品推荐
相关产品推荐

