closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

Bad type annotation when documenting an optional parameter value

Open customcommander opened this issue 4 years ago • 2 comments

With Closure Compiler v20210106 and the following flags:

--language_in=ECMASCRIPT_NEXT
--compilation_level ADVANCED
--js tmp/wrap.js 

This works fine:

/**
 * @param {string} str
 * @param {string} [prefix=aa]
 * @param {string} [suffix=bb]
 */
function wrap1(str, prefix="aa", suffix="bb") {
  return prefix + str + suffix;
}

window.wrap1 = wrap1;

However this:

/**
 * @param {string} str
 * @param {string} [prefix=(]
 * @param {string} [suffix=)]
 */
function wrap2(str, prefix="(", suffix=")") {
  return prefix + str + suffix;
}

window.wrap2 = wrap2;

produces the following warnings:

tmp/wrap.js:12:27: WARNING - [JSC_TYPE_PARSE_ERROR] Bad type annotation. missing closing ] See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
  12|  * @param {string} [prefix=(]
                                 ^

tmp/wrap.js:13:27: WARNING - [JSC_TYPE_PARSE_ERROR] Bad type annotation. missing closing ] See https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler for more information.
  13|  * @param {string} [suffix=)]
                                 ^

0 error(s), 2 warning(s), 100.0% typed

It looks like the compiler is confused about the sequence of opening and closing "brackets". However in this case both ( and ) are the default values for prefix and suffix. To verify this theory I've created wrap3 with both prefix and suffix defaulting to ] so as to make it look like the sequence is "complete":

/**
 * @param {string} str
 * @param {string} [prefix=]]
 * @param {string} [suffix=]]
 */
function wrap3(str, prefix="]", suffix="]") {
  return prefix + str + suffix;
}

window.wrap3 = wrap3;

The above function generates no warnings or errors.


Complete file:

/**
 * @param {string} str
 * @param {string} [prefix=aa]
 * @param {string} [suffix=bb]
 */
function wrap1(str, prefix="aa", suffix="bb") {
  return prefix + str + suffix;
}

/**
 * @param {string} str
 * @param {string} [prefix=(]
 * @param {string} [suffix=)]
 */
function wrap2(str, prefix="(", suffix=")") {
  return prefix + str + suffix;
}

/**
 * @param {string} str
 * @param {string} [prefix=]]
 * @param {string} [suffix=]]
 */
function wrap3(str, prefix="]", suffix="]") {
  return prefix + str + suffix;
}

window.wrap1 = wrap1;
window.wrap2 = wrap2;
window.wrap3 = wrap3;

customcommander avatar Feb 07 '21 17:02 customcommander

Wow. I didn't know the compiler would even allow this syntax of putting the parameter's name in square brackets.

If you also supply the "=default" part AFAICT the compiler completely throws that away.

The only test for this syntax is called testUnsupportedJsDocSyntax1()

https://github.com/google/closure-compiler/blob/3ff3fd061e164dbd825def388d53f3c63d341ed4/test/com/google/javascript/jscomp/parsing/JsDocInfoParserTest.java#L5073

I'm guessing that this [name=defaultValue] syntax is something that exists in the OpenSource JSDoc standard, which is definitely not the same as the JSDoc that closure-compiler uses. I think some little effort must have been made to support this bit of that syntax years ago. Unsurprisingly, there's a bug in it.

brad4d avatar Feb 08 '21 17:02 brad4d

I'm guessing that this [name=defaultValue] syntax is something that exists in the OpenSource JSDoc standard,

I believe that's right.

When I run yarn -s jsdoc -X tmp/wrap.js it produces no warnings or errors. In case it helps here's an example of what JSDoc produces:

{
  "comment": "/**\n * @param {string} str\n * @param {string} [prefix=(]\n * @param {string} [suffix=)]\n */",
  "meta": {
    "range": [
      267,
      346
    ],
    "filename": "wrap.js",
    "lineno": 15,
    "columnno": 0,
    "path": "/workspace/dev/tmp",
    "code": {
      "id": "astnode100000018",
      "name": "wrap2",
      "type": "FunctionDeclaration",
      "paramnames": [
        "str",
        "prefix",
        "suffix"
      ]
    }
  },
  "params": [
    {
      "type": {
        "names": [
          "string"
        ]
      },
      "name": "str"
    },
    {
      "type": {
        "names": [
          "string"
        ]
      },
      "optional": true,
      "defaultvalue": "(",
      "name": "prefix"
    },
    {
      "type": {
        "names": [
          "string"
        ]
      },
      "optional": true,
      "defaultvalue": ")",
      "name": "suffix"
    }
  ],
  "name": "wrap2",
  "longname": "wrap2",
  "kind": "function",
  "scope": "global"
}

customcommander avatar Feb 08 '21 18:02 customcommander