Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript: function/method named 'get' or 'set' causes various tag generation issues #3641

Closed
the-noob-of-northrose opened this issue Feb 6, 2023 · 3 comments
Assignees

Comments

@the-noob-of-northrose
Copy link

the-noob-of-northrose commented Feb 6, 2023

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)


❌ 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 fieldBefore and bar getting 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
@masatake
Copy link
Member

masatake commented Feb 7, 2023

Quite a helpful bug report! Thank you.

@jafl jafl self-assigned this Feb 7, 2023
@jafl
Copy link
Contributor

jafl commented Feb 9, 2023

Cases 3 & 4 work with the latest version of ctags. I will debug case 1 when I can find some time.

@jafl
Copy link
Contributor

jafl commented Jul 5, 2023

Case 1 fixed in #3761

@jafl jafl closed this as completed Jul 5, 2023
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants