Universal Ctags处理TypeScript类成员时,含函数调用的内联初始化导致后续成员标签丢失
我在使用Universal Ctags 5.9.0处理TypeScript类文件时遇到了一个解析异常问题,当类属性使用带括号的函数调用进行内联初始化时,后续的类成员(包括属性和方法)无法被正确生成标签。以下是详细的复现过程、现象和测试细节:
测试环境
- 工具版本:Universal Ctags 5.9.0
- 测试文件:
fl.ts(TypeScript格式) - 执行命令:
ctags --languages=typescript fl.ts
1. 初始代码与异常表现
首先是测试用的fl.ts代码:
class MyClass { attributeBeforeInitializationLine: string; attributeInlineInitialization = myFunction(); attributeAfterInitializationLine: string; myfunction_1() { let y = 1; } myfunction_2() { let z = 1; } } class MyClass_2 { attributeMyClass2: int; }
执行ctags命令后,生成的标签文件只包含MyClass、attributeBeforeInitializationLine、attributeInlineInitialization这三个属于MyClass的标签,attributeAfterInitializationLine和两个方法myfunction_1、myfunction_2完全缺失,但MyClass_2的所有标签都正常生成。
对应的标签文件内容如下:
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ !_TAG_OUTPUT_FILESEP slash /slash or backslash/ !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ !_TAG_PROC_CWD /home/fl/tmp/ // !_TAG_PROGRAM_AUTHOR Universal Ctags Team // !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ !_TAG_PROGRAM_URL https://ctags.io/ /official site/ !_TAG_PROGRAM_VERSION 5.9.0 // MyClass fl.ts /^class MyClass {$/;" c MyClass_2 fl.ts /^class MyClass_2 {$/;" c attributeBeforeInitializationLine fl.ts /^ attributeBeforeInitializationLine: string;$/;" p class:MyClass attributeInlineInitialization fl.ts /^ attributeInlineInitialization = myFunction();$/;" p class:MyClass attributeMyClass2 fl.ts /^ attributeMyClass2: int;$/;" p class:MyClass_2 y fl.ts /^ let y = 1;$/;" v z fl.ts /^ let z = 1;$/;" v
奇怪的是,函数内部的局部变量y和z反而被生成了标签,但类方法却完全被忽略。
2. 去掉括号后的正常情况
如果将内联初始化语句中的括号去掉,修改为仅引用函数名:
attributeInlineInitialization = myFunction;
重新执行ctags命令后,所有标签都能正常生成,包括attributeAfterInitializationLine和两个类方法:
!_TAG_PROGRAM_VERSION 5.9.0 // MyClass fl.ts /^class MyClass {$/;" c MyClass_2 fl.ts /^class MyClass_2 {$/;" c attributeAfterInitializationLine fl.ts /^ attributeAfterInitializationLine: string;$/;" p class:MyClass attributeBeforeInitializationLine fl.ts /^ attributeBeforeInitializationLine: string;$/;" p class:MyClass attributeInlineInitialization fl.ts /^ attributeInlineInitialization = myFunction;$/;" p class:MyClass attributeMyClass2 fl.ts /^ attributeMyClass2: int;$/;" p class:MyClass_2 myfunction_1 fl.ts /^ myfunction_1() {$/;" m class:MyClass myfunction_2 fl.ts /^ myfunction_2() {$/;" m class:MyClass
3. 调整属性位置后的现象
如果把attributeAfterInitializationLine移到带括号初始化的属性前面,代码如下:
class MyClass { attributeBeforeInitializationLine: string; attributeAfterInitializationLine: string; attributeInlineInitialization = myFunction(); myfunction_1() { let y = 1; } myfunction_2() { let z = 1; } } class MyClass_2 { attributeMyClass2: int; }
此时生成的标签中,attributeAfterInitializationLine能被正常识别,但myfunction_1和myfunction_2仍然缺失,局部变量y和z的标签依然存在:
!_TAG_PROGRAM_VERSION 5.9.0 // MyClass fl.ts /^class MyClass {$/;" c MyClass_2 fl.ts /^class MyClass_2 {$/;" c attributeAfterInitializationLine fl.ts /^ attributeAfterInitializationLine: string;$/;" p class:MyClass attributeBeforeInitializationLine fl.ts /^ attributeBeforeInitializationLine: string;$/;" p class:MyClass attributeInlineInitialization fl.ts /^ attributeInlineInitialization = myFunction();$/;" p class:MyClass attributeMyClass2 fl.ts /^ attributeMyClass2: int;$/;" p class:MyClass_2 y fl.ts /^ let y = 1;$/;" v z fl.ts /^ let z = 1;$/;" v
问题总结与临时解决办法
从测试结果可以确定,这是Universal Ctags TypeScript解析器的一个bug:当解析到形如attr = func()的内联初始化语句时,解析器的状态机出现异常,无法正确识别后续的类成员(属性和方法),但仍会继续解析函数内部的局部变量。
临时解决方案
- 生成标签前,临时将带括号的初始化语句修改为
attr = func(去掉括号),生成标签后再恢复原代码; - 手动编辑标签文件,补充缺失的类成员标签;
- 尝试升级到Universal Ctags的最新版本,看是否已修复该bug。
备注:内容来源于stack exchange,提问作者FrankL

