Repository: eslint/doctrine
Pull request #196 (2017-05-31 18:17:23)

adds nyc and a newer version of mocha to accurately report coverage
Hi! I'm an [`istanbuljs`](https://github.com/istanbuljs/istanbuljs) member and I noticed that your coverage badge is broken. Oh noes!

I took the liberty of adding `nyc`, the [IstanbulJS 2.0](https://github.com/istanbuljs/nyc) CLI, and upgrading mocha [to remove all of v2's extraneous dependencies and remove the DoS warnings etv etc].

I removed the Makefile.js and replaced it with much simpler `npm` scripts, for readability / maintainability's sake. 

I'm open feedback on all these changes but hopefully they're pretty simple and self explanatory :) any feedback would definitely help us improve getting `nyc` out there and better supported for everyone. Thanks!

jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/doctrine?pullRequest=196) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla.js.foundation/eslint/doctrine?pullRequest=196) before we can accept your contribution.<br/>
coveralls: 
[![Coverage Status](https://coveralls.io/builds/11767807/badge)](https://coveralls.io/builds/11767807)

Changes Unknown when pulling **c336f4950057acd72ba38d843d5de0a48cc91c73 on JaKXz:chore/coverage** into ** on eslint:master**.

medanat: I would suggest adding a `mocha.opts` file with 

```
-R dot 
-t 4000
```

to maintain the old configuration
coveralls: 
[![Coverage Status](https://coveralls.io/builds/11768042/badge)](https://coveralls.io/builds/11768042)

Changes Unknown when pulling **c69c6d826933f6103108d1caf7c8104a4f7972a3 on JaKXz:chore/coverage** into ** on eslint:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/11768042/badge)](https://coveralls.io/builds/11768042)

Changes Unknown when pulling **c69c6d826933f6103108d1caf7c8104a4f7972a3 on JaKXz:chore/coverage** into ** on eslint:master**.


Patch:
diff --git a/.gitignore b/.gitignore
index 5ca5bc3..44f5a84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@ node_modules
 .idea
 *.iml
 coverage
+.nyc_output
 .cache
 npm-debug.log
diff --git a/Makefile.js b/Makefile.js
deleted file mode 100644
index c814824..0000000
--- a/Makefile.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @fileoverview Build file
- * @author nzakas
- */
-/* global exec, exit, find, target*/
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-require("shelljs/make");
-
-var nodeCLI = require("shelljs-nodecli");
-
-//------------------------------------------------------------------------------
-// Data
-//------------------------------------------------------------------------------
-
-var NODE_MODULES = "./node_modules/",
-
-    // Utilities - intentional extra space at the end of each string
-    MOCHA = NODE_MODULES + "mocha/bin/_mocha ",
-
-    // Files
-    /* eslint-disable no-use-before-define */
-    TEST_FILES = find("test/").filter(fileType("js")).join(" "),
-    /* eslint-enable no-use-before-define */
-
-    // Settings
-    MOCHA_TIMEOUT = 4000;
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Generates a function that matches files with a particular extension.
- * @param {string} extension The file extension (i.e. "js")
- * @returns {Function} The function to pass into a filter method.
- * @private
- */
-function fileType(extension) {
-    return function(filename) {
-        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
-    };
-}
-
-/**
- * Splits a command result to separate lines.
- * @param {string} result The command result string.
- * @returns {array} The separated lines.
- */
-function splitCommandResultToLines(result) {
-    return result.trim().split("\n");
-}
-
-//------------------------------------------------------------------------------
-// Tasks
-//------------------------------------------------------------------------------
-
-target.all = function() {
-    target.test();
-};
-
-target.test = function() {
-
-    var errors = 0,
-        lastReturn;
-
-    lastReturn = nodeCLI.exec("istanbul", "cover", MOCHA, "-- -R dot -t " + MOCHA_TIMEOUT, "-c", TEST_FILES);
-    if (lastReturn.code !== 0) {
-        errors++;
-    }
-
-    lastReturn = nodeCLI.exec("istanbul", "check-coverage", "--statement 99 --branch 97 --function 100 --lines 99");
-    if (lastReturn.code !== 0) {
-        errors++;
-    }
-
-    if (errors) {
-        exit(1);
-    }
-
-};
-
diff --git a/lib/doctrine.js b/lib/doctrine.js
index 831aff2..a5804df 100644
--- a/lib/doctrine.js
+++ b/lib/doctrine.js
@@ -266,7 +266,7 @@
 
         function scanIdentifier(last) {
             var identifier;
-            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index))) {
+            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {
                 return null;
             }
             identifier = advance();
@@ -294,13 +294,13 @@
                 return null;
             }
 
-            if (allowBrackets && source.charCodeAt(index) === 0x5B  /* '[' */) {
-                useBrackets = true;
-                name = advance();
-            }
-
-            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index))) {
-                return null;
+            if (source.charCodeAt(index) === 0x5B  /* '[' */) {
+                if (allowBrackets) {
+                    useBrackets = true;
+                    name = advance();
+                } else {
+                    return null;
+                }
             }
 
             name += scanIdentifier(last);
@@ -523,8 +523,8 @@
                         // extract the default value if there is one
                         // example: @param {string} [somebody=John Doe] description
                         assign = name.substring(1, name.length - 1).split('=');
-                        if (assign[1]) {
-                            this._tag['default'] = assign[1];
+                        if (assign.length > 1) {
+                            this._tag['default'] = assign.slice(1).join('=');
                         }
                         this._tag.name = assign[0];
 
@@ -607,13 +607,13 @@
             return true;
         };
 
-        TagParser.prototype.parseThis = function parseAccess() {
-            // this name may be a name expression (e.g. {foo.bar})
-            // or a name path (e.g. foo.bar)
+        TagParser.prototype.parseThis = function parseThis() {
+            // this name may be a name expression (e.g. {foo.bar}),
+            // an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)
             var value = trim(sliceSource(source, index, this._last));
             if (value && value.charAt(0) === '{') {
                 var gotType = this.parseType();
-                if (gotType && this._tag.type.type === 'NameExpression') {
+                if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {
                     this._tag.name = this._tag.type.name;
                     return true;
                 } else {
diff --git a/package.json b/package.json
index 71eec5c..a57c990 100644
--- a/package.json
+++ b/package.json
@@ -35,10 +35,10 @@
     "dateformat": "^1.0.11",
     "eslint": "^1.10.3",
     "eslint-release": "^0.10.0",
-    "istanbul": "^0.4.1",
     "linefix": "^0.1.1",
-    "mocha": "^2.3.3",
+    "mocha": "^3.4.2",
     "npm-license": "^0.3.1",
+    "nyc": "^10.3.2",
     "semver": "^5.0.3",
     "shelljs": "^0.5.3",
     "shelljs-nodecli": "^0.1.1",
@@ -46,7 +46,9 @@
   },
   "license": "Apache-2.0",
   "scripts": {
-    "test": "npm run lint && node Makefile.js test",
+    "pretest": "npm run lint",
+    "test": "nyc mocha",
+    "coveralls": "nyc report --reporter=text-lcov | coveralls",
     "lint": "eslint lib/",
     "release": "eslint-release",
     "ci-release": "eslint-ci-release",


Test patch:
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 0000000..f3a9530
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1,2 @@
+--reporter dot
+--timeout 4000
diff --git a/test/parse.js b/test/parse.js
index abcde95..51886db 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -498,6 +498,41 @@ describe('parse', function () {
         });
     });
 
+    it('param with array indexes', function () {
+        var res = doctrine.parse(
+            [
+                "/**",
+                " * @param {String} user.0",
+                "*/"
+            ].join('\n'), { unwrap: true });
+        res.tags.should.have.length(1);
+        res.tags[0].should.have.property('title', 'param');
+        res.tags[0].should.have.property('name', 'user.0');
+        res.tags[0].should.have.property('type');
+        res.tags[0].type.should.eql({
+            type: 'NameExpression',
+            name: 'String'
+        });
+    });
+
+    it('param with array indexes and descriptions', function () {
+        var res = doctrine.parse(
+            [
+                "/**",
+                " * @param {String} user.0 The first element",
+                "*/"
+            ].join('\n'), { unwrap: true });
+        res.tags.should.have.length(1);
+        res.tags[0].should.have.property('title', 'param');
+        res.tags[0].should.have.property('name', 'user.0');
+        res.tags[0].should.have.property('type');
+        res.tags[0].should.have.property('description', 'The first element');
+        res.tags[0].type.should.eql({
+            type: 'NameExpression',
+            name: 'String'
+        });
+    });
+
     it('arg with properties', function () {
         var res = doctrine.parse(
             [
@@ -1172,9 +1207,25 @@ describe('parse', function () {
             ].join('\n'), { unwrap: true });
         res.tags.should.have.length(1);
         res.tags[0].should.have.property('title', 'this');
+        res.tags[0].type.should.have.property('type', 'NameExpression');
         res.tags[0].should.have.property('name', 'thingName.name');
     });
 
+    it('this with UnionType expression', function () {
+        var res = doctrine.parse(
+            [
+              "/**",
+              " * @this {thingName.name|FooBar}",
+              "*/"
+            ].join('\n'), { unwrap: true });
+        res.tags.should.have.length(1);
+        res.tags[0].should.have.property('title', 'this');
+        res.tags[0].type.should.have.property('type', 'UnionType');
+        res.tags[0].type.elements.should.have.length(2);
+        res.tags[0].type.elements.should.containEql({type: 'NameExpression', name: 'thingName.name'});
+        res.tags[0].type.elements.should.containEql({type: 'NameExpression', name: 'FooBar'});
+    });
+
     it('this error with type application', function () {
         var res = doctrine.parse(
             [
@@ -2271,6 +2322,28 @@ describe('optional params', function() {
         });
     });
 
+    it('default arrow functions', function() {
+        doctrine.parse(
+            ["/**", " * @param {Function} [fn=()=>{}] some description", " */"].join('\n'),
+            {unwrap: true, sloppy: true}
+        ).should.eql({
+            description: "",
+            tags: [{
+                "title": "param",
+                "description": "some description",
+                "type": {
+                    "type": "OptionalType",
+                    "expression": {
+                        "type": "NameExpression",
+                        "name": "Function"
+                    }
+                },
+                "name": "fn",
+                "default": "()=>{}"
+            }]
+        });
+    });
+
     it('line numbers', function() {
         var res = doctrine.parse(
             [