JavaScript: function/method named 'get' or 'set' causes various tag generation issues
Having a function/method named 'get' or 'set' seems to break the parser in a couple of situations (possibly/probably related to how the parser is trying to handle ES6 class getters/setters).
All of my example cases illustrate only with 'get', but the same symptoms occur if you replace 'get' with 'set'.
General Information
The name of the parser: jscript
The command line you used to run ctags:
$ ctags --options=NONE <filename>
The version of ctags (I realize I'm not up-to-date but a perusal of changes to the jscript parser between then and now suggested that this issue should still be present):
$ ctags --version
Universal Ctags 5.9.0, Copyright (C) 2015 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
Compiled: Sep 3 2021, 18:12:18
URL: https://ctags.io/
Optional compiled features: +wildcards, +regex, +gnulib_regex, +iconv, +option-directory, +xpath, +json, +interactive, +sandbox, +yaml, +packcc, +optscript
How do you get ctags binary: Ubuntu 22.04 repository (via apt)
:x: Case 1: Not using ES6 class, "normal" function declaration of 'get'
The 'get' function gets no tag. Neither having 'get' accept arguments nor putting anything in its code block makes a difference.
The content of input file:
function definedWithFunctionBefore() {};
const functionExpressionBefore = () => {};
let variableBefore = "foo";
function get() {};
function definedWithFunctionAfter() {};
const functionExpressionAfter = () => {};
let variableAfter = "bar";
The tags output you are not satisfied with:
definedWithFunctionAfter NonClass.js /^function definedWithFunctionAfter() {};$/;" f
definedWithFunctionBefore NonClass.js /^function definedWithFunctionBefore() {};$/;" f
functionExpressionAfter NonClass.js /^const functionExpressionAfter = () => {};$/;" C
functionExpressionBefore NonClass.js /^const functionExpressionBefore = () => {};$/;" C
variableAfter NonClass.js /^let variableAfter = "bar";$/;" v
variableBefore NonClass.js /^let variableBefore = "foo";$/;" v
The tags output you expect: Previous output plus the following:
get NonClass.js /^function get() {};$/;" f
✔️ Case 2: Not using ES6 class, 'get' as function expression
All tags generated as expected
Input file:
function definedWithFunctionBefore() {};
const functionExpressionBefore = () => {};
let variableBefore = "foo";
const get = function () {};
// also works as arrow function // const get = () => {};
function definedWithFunctionAfter() {};
const functionExpressionAfter = () => {};
let variableAfter = "bar";
Tags expected == tags actual:
definedWithFunctionAfter NonClass.js /^function definedWithFunctionAfter() {};$/;" f
definedWithFunctionBefore NonClass.js /^function definedWithFunctionBefore() {};$/;" f
functionExpressionAfter NonClass.js /^const functionExpressionAfter = () => {};$/;" C
functionExpressionBefore NonClass.js /^const functionExpressionBefore = () => {};$/;" C
get NonClass.js /^const get = function () {};$/;" f
variableAfter NonClass.js /^let variableAfter = "bar";$/;" v
variableBefore NonClass.js /^let variableBefore = "foo";$/;" v
❌ Case 3: 'get' as a method in an ES6 class, no arguments
- No tag for 'get'
- No tag for any method or class field following 'get'
Input file:
class TestClass {
methodBefore() {};
fieldBefore = "foo";
get() {};
methodAfter() {};
fieldAfter = "bar";
}
The tags output you are not satisfied with:
TestClass TestClass.js /^class TestClass {$/;" c
fieldBefore TestClass.js /^ fieldBefore = "foo";$/;" M class:TestClass
methodBefore TestClass.js /^ methodBefore() {};$/;" m class:TestClass
The tags output you expect: Previous output plus the following:
fieldAfter TestClass.js /^ fieldAfter = "bar";$/;" M class:TestClass
get TestClass.js /^ get() {};$/;" m class:TestClass
methodAfter TestClass.js /^ methodAfter() {};$/;" m class:TestClass
❌ Case 4: 'get' as a method in an ES6 class, with argument
- No tag for 'get'
- No tag for any method or class field following 'get'
- Both
fieldBeforeandbargetting field tags from the definition of method 'get' despite lack of tag for 'get' itself
Input file:
class TestClass {
methodBefore() {};
fieldBefore = "foo";
get(bar) {return this.fieldBefore + bar};
methodAfter() {};
fieldAfter = "baz";
}
The tags output you are not satisfied with:
TestClass TestClass.js /^class TestClass {$/;" c
bar TestClass.js /^ get(bar) {return this.fieldBefore + bar};$/;" M class:TestClass
fieldBefore TestClass.js /^ fieldBefore = "foo";$/;" M class:TestClass
fieldBefore TestClass.js /^ get(bar) {return this.fieldBefore + bar};$/;" M class:TestClass
methodBefore TestClass.js /^ methodBefore() {};$/;" m class:TestClass
The tags output you expect:
TestClass TestClass.js /^class TestClass {$/;" c
fieldAfter TestClass.js /^ fieldAfter = "baz";$/;" M class:TestClass
fieldBefore TestClass.js /^ fieldBefore = "foo";$/;" M class:TestClass
get TestClass.js /^ get(bar) {return this.fieldBefore + bar};$/;" m class:TestClass
methodAfter TestClass.js /^ methodAfter() {};$/;" m class:TestClass
methodBefore TestClass.js /^ methodBefore() {};$/;" m class:TestClass
✔️ Case 5: proper ES6 getter
All tags generated as expected
Input file:
class TestClass {
methodBefore() {};
fieldBefore = "foo";
get es6Getter() {return "you got " + this.fieldBefore};
methodAfter(bar) {return bar};
fieldAfter = "baz";
}
Tags expected == tags actual:
TestClass TestClass.js /^class TestClass {$/;" c
es6Getter TestClass.js /^ get es6Getter() {return "you got " + this.fieldBefore};$/;" G class:TestClass
fieldAfter TestClass.js /^ fieldAfter = "baz";$/;" M class:TestClass
fieldBefore TestClass.js /^ fieldBefore = "foo";$/;" M class:TestClass
methodAfter TestClass.js /^ methodAfter(bar) {return bar};$/;" m class:TestClass
methodBefore TestClass.js /^ methodBefore() {};$/;" m class:TestClass
Quite a helpful bug report! Thank you.
Cases 3 & 4 work with the latest version of ctags. I will debug case 1 when I can find some time.
Case 1 fixed in https://github.com/universal-ctags/ctags/pull/3761