Repository: eslint/eslint (js)

Issue:
Can't lint files with square brackets in the name
**Tell us about your environment**

MacOS 10.14.5 (18F132)
zsh 5.3 (x86_64-apple-darwin18.0)

* **ESLint Version:** v6.0.1
* **Node Version:** v8.15.0
* **npm Version:** 6.9.0

**What parser (default, Babel-ESLint, etc.) are you using?**
default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
module.exports = {
  "env": {
    "browser": true,
    "node": true,
    "es6": true,
  },
  "extends": [
    "eslint:recommended",
    "plugin:prettier/recommended",
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly",
  },
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module",
  },
  "rules": {
    "no-unused-vars": ["error", {args: "none"}],
  },
}
```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
No source, the bug happens even if the file is empty.

<!-- Paste the command you used to run ESLint: -->
```bash
$ cat src/routes/blog/\[slug\].svelte
console.log('x',)
$ npx eslint src/routes/blog/\[slug\].svelte
No files matching 'src/routes/blog/[slug].svelte' were found.
$ npx eslint src/routes/blog/[slug].svelte
No files matching 'src/routes/blog/[slug].svelte' were found.
$ npx eslint src/routes/blog/\\[slug\\].svelte
No files matching 'src/routes/blog/\[slug\].svelte' were found.
```

**What did you expect to happen?**

That eslint would lint the file.

**What actually happened? Please include the actual, raw output from ESLint.**

Eslint was unable to find the file.

**Are you willing to submit a pull request to fix this bug?**

I could give it a shot, but I'm probably not the best person to solve it.


Pull request description:
Fix: glob processing (fixes #11940)
**What is the purpose of this pull request? (put an "X" next to item)**

[X] Bug fix: #11940

**What changes did you make? (Give an overview)**

This PR fixes glob processing to iterate target files. The new way is similar to how shell passes globs to programs. I.e., if there are files that the glob matched, it adopts the matched files. Otherwise, it adopts the glob string as is.

**Is there anything you'd like reviewers to focus on?**

Nothing in particular.


Patch:
diff --git a/Makefile.js b/Makefile.js
index 9bb9e12ad..02386d2f4 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -56,8 +56,8 @@ const NODE = "node ", // intentional extra space
     TEMP_DIR = "./tmp/",
     DEBUG_DIR = "./debug/",
     BUILD_DIR = "build",
-    DOCS_DIR = "../eslint.github.io/docs",
-    SITE_DIR = "../eslint.github.io/",
+    DOCS_DIR = "../website/docs",
+    SITE_DIR = "../website/",
     PERF_TMP_DIR = path.join(TEMP_DIR, "eslint", "performance"),
 
     // Utilities - intentional extra space at the end of each string
@@ -139,7 +139,7 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
         now = new Date(),
         month = now.getMonth() + 1,
         day = now.getDate(),
-        filename = `../eslint.github.io/_posts/${now.getFullYear()}-${
+        filename = `../website/_posts/${now.getFullYear()}-${
             month < 10 ? `0${month}` : month}-${
             day < 10 ? `0${day}` : day}-eslint-v${
             releaseInfo.version}-released.md`;
@@ -156,8 +156,8 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
  */
 function generateFormatterExamples(formatterInfo, prereleaseVersion) {
     const output = ejs.render(cat("./templates/formatter-examples.md.ejs"), formatterInfo);
-    let filename = "../eslint.github.io/docs/user-guide/formatters/index.md",
-        htmlFilename = "../eslint.github.io/docs/user-guide/formatters/html-formatter-example.html";
+    let filename = "../website/docs/user-guide/formatters/index.md",
+        htmlFilename = "../website/docs/user-guide/formatters/html-formatter-example.html";
 
     if (prereleaseVersion) {
         filename = filename.replace("/docs", `/docs/${prereleaseVersion}`);
@@ -176,7 +176,7 @@ function generateFormatterExamples(formatterInfo, prereleaseVersion) {
  * @returns {void}
  */
 function generateRuleIndexPage() {
-    const outputFile = "../eslint.github.io/_data/rules.yml",
+    const outputFile = "../website/_data/rules.yml",
         categoryList = "conf/category-list.json",
         categoriesData = JSON.parse(cat(path.resolve(categoryList)));
 
@@ -216,7 +216,7 @@ function generateRuleIndexPage() {
 }
 
 /**
- * Creates a git commit and tag in an adjacent `eslint.github.io` repository, without pushing it to
+ * Creates a git commit and tag in an adjacent `website` repository, without pushing it to
  * the remote. This assumes that the repository has already been modified somehow (e.g. by adding a blogpost).
  * @param {string} [tag] The string to tag the commit with
  * @returns {void}
@@ -237,7 +237,7 @@ function commitSiteToGit(tag) {
 }
 
 /**
- * Publishes the changes in an adjacent `eslint.github.io` repository to the remote. The
+ * Publishes the changes in an adjacent `website` repository to the remote. The
  * site should already have local commits (e.g. from running `commitSiteToGit`).
  * @returns {void}
  */
@@ -251,7 +251,7 @@ function publishSite() {
 
 /**
  * Updates the changelog, bumps the version number in package.json, creates a local git commit and tag,
- * and generates the site in an adjacent `eslint.github.io` folder.
+ * and generates the site in an adjacent `website` folder.
  * @returns {void}
  */
 function generateRelease() {
@@ -266,7 +266,7 @@ function generateRelease() {
 
 /**
  * Updates the changelog, bumps the version number in package.json, creates a local git commit and tag,
- * and generates the site in an adjacent `eslint.github.io` folder.
+ * and generates the site in an adjacent `website` folder.
  * @param {string} prereleaseId The prerelease identifier (alpha, beta, etc.)
  * @returns {void}
  */
@@ -303,7 +303,7 @@ function generatePrerelease(prereleaseId) {
 }
 
 /**
- * Publishes a generated release to npm and GitHub, and pushes changes to the adjacent `eslint.github.io` repo
+ * Publishes a generated release to npm and GitHub, and pushes changes to the adjacent `website` repo
  * to remote repo.
  * @returns {void}
  */
diff --git a/README.md b/README.md
index efc9d7a48..18d15249f 100644
--- a/README.md
+++ b/README.md
@@ -258,9 +258,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
 <!--sponsorsstart-->
 <h3>Gold Sponsors</h3>
-<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/logo.png" alt="Shopify" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/salesforce/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/logo.png" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/fbopensource/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
-<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/logo.png" alt="JSHeroes " height="32"></a> <a href="https://faithlife.com/ref/about"><img src="https://images.opencollective.com/faithlife/logo.png" alt="Faithlife" height="32"></a></p>
+<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/salesforce/853ecef/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/001a341/logo.png" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
+<p><a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a> <a href="https://faithlife.com/ref/about"><img src="https://images.opencollective.com/faithlife/534b832/logo.png" alt="Faithlife" height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/docs/rules/dot-location.md b/docs/rules/dot-location.md
index 1fe4a3129..82f0d94a5 100644
--- a/docs/rules/dot-location.md
+++ b/docs/rules/dot-location.md
@@ -43,7 +43,13 @@ Examples of **correct** code for the default `"object"` option:
 
 var foo = object.
 property;
-var bar = object.property;
+
+var bar = (
+    object
+).
+property;
+
+var baz = object.property;
 ```
 
 ### property
diff --git a/docs/rules/object-curly-newline.md b/docs/rules/object-curly-newline.md
index 657e4586f..4e1cce704 100644
--- a/docs/rules/object-curly-newline.md
+++ b/docs/rules/object-curly-newline.md
@@ -15,7 +15,7 @@ This rule has either a string option:
 
 Or an object option:
 
-* `"multiline": true` requires line breaks if there are line breaks inside properties or between properties
+* `"multiline": true` requires line breaks if there are line breaks inside properties or between properties. Otherwise, it disallows line breaks.
 * `"minProperties"` requires line breaks if the number of properties is at least the given integer. By default, an error will also be reported if an object contains linebreaks and has fewer properties than the given integer. However, the second behavior is disabled if the `consistent` option is set to `true`
 * `"consistent": true` (default) requires that either both curly braces, or neither, directly enclose newlines. Note that enabling this option will also change the behavior of the `minProperties` option. (See `minProperties` above for more information)
 
diff --git a/lib/cli-engine/file-enumerator.js b/lib/cli-engine/file-enumerator.js
index 2840d9fe2..a027359ae 100644
--- a/lib/cli-engine/file-enumerator.js
+++ b/lib/cli-engine/file-enumerator.js
@@ -292,26 +292,18 @@ class FileEnumerator {
     _iterateFiles(pattern) {
         const { cwd, globInputPaths } = internalSlotsMap.get(this);
         const absolutePath = path.resolve(cwd, pattern);
-
-        if (globInputPaths && isGlobPattern(pattern)) {
-            return this._iterateFilesWithGlob(
-                absolutePath,
-                dotfilesPattern.test(pattern)
-            );
-        }
-
+        const isDot = dotfilesPattern.test(pattern);
         const stat = statSafeSync(absolutePath);
 
         if (stat && stat.isDirectory()) {
-            return this._iterateFilesWithDirectory(
-                absolutePath,
-                dotfilesPattern.test(pattern)
-            );
+            return this._iterateFilesWithDirectory(absolutePath, isDot);
         }
-
         if (stat && stat.isFile()) {
             return this._iterateFilesWithFile(absolutePath);
         }
+        if (globInputPaths && isGlobPattern(pattern)) {
+            return this._iterateFilesWithGlob(absolutePath, isDot);
+        }
 
         return [];
     }
diff --git a/lib/rules/arrow-body-style.js b/lib/rules/arrow-body-style.js
index 6d84c7adf..8d3b40003 100644
--- a/lib/rules/arrow-body-style.js
+++ b/lib/rules/arrow-body-style.js
@@ -175,10 +175,10 @@ module.exports = {
                             }
 
                             /*
-                             * If the first token of the reutrn value is `{`,
+                             * If the first token of the reutrn value is `{` or the return value is a sequence expression,
                              * enclose the return value by parentheses to avoid syntax error.
                              */
-                            if (astUtils.isOpeningBraceToken(firstValueToken)) {
+                            if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
                                 fixes.push(
                                     fixer.insertTextBefore(firstValueToken, "("),
                                     fixer.insertTextAfter(lastValueToken, ")")
diff --git a/lib/rules/dot-location.js b/lib/rules/dot-location.js
index b9857ae23..c2e734a1a 100644
--- a/lib/rules/dot-location.js
+++ b/lib/rules/dot-location.js
@@ -54,29 +54,31 @@ module.exports = {
          */
         function checkDotLocation(obj, prop, node) {
             const dot = sourceCode.getTokenBefore(prop);
-            const textBeforeDot = sourceCode.getText().slice(obj.range[1], dot.range[0]);
+
+            // `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
+            const tokenBeforeDot = sourceCode.getTokenBefore(dot);
+
+            const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
             const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
 
-            if (dot.type === "Punctuator" && dot.value === ".") {
-                if (onObject) {
-                    if (!astUtils.isTokenOnSameLine(obj, dot)) {
-                        const neededTextAfterObj = astUtils.isDecimalInteger(obj) ? " " : "";
-
-                        context.report({
-                            node,
-                            loc: dot.loc.start,
-                            messageId: "expectedDotAfterObject",
-                            fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
-                        });
-                    }
-                } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
+            if (onObject) {
+                if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
+                    const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
+
                     context.report({
                         node,
                         loc: dot.loc.start,
-                        messageId: "expectedDotBeforeProperty",
-                        fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
+                        messageId: "expectedDotAfterObject",
+                        fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
                     });
                 }
+            } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
+                context.report({
+                    node,
+                    loc: dot.loc.start,
+                    messageId: "expectedDotBeforeProperty",
+                    fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
+                });
             }
         }
 
@@ -86,7 +88,9 @@ module.exports = {
          * @returns {void}
          */
         function checkNode(node) {
-            checkDotLocation(node.object, node.property, node);
+            if (!node.computed) {
+                checkDotLocation(node.object, node.property, node);
+            }
         }
 
         return {
diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js
index 1aef4c162..78ae7bc01 100644
--- a/lib/rules/utils/ast-utils.js
+++ b/lib/rules/utils/ast-utils.js
@@ -37,6 +37,8 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
 // A set of node types that can contain a list of statements
 const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
 
+const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
+
 /**
  * Checks reference if is non initializer and writable.
  * @param {Reference} reference - A reference to check.
@@ -283,6 +285,16 @@ function isCommaToken(token) {
     return token.value === "," && token.type === "Punctuator";
 }
 
+/**
+ * Checks if the given token is a dot token or not.
+ *
+ * @param {Token} token - The token to check.
+ * @returns {boolean} `true` if the token is a dot token.
+ */
+function isDotToken(token) {
+    return token.value === "." && token.type === "Punctuator";
+}
+
 /**
  * Checks if the given token is a semicolon token or not.
  *
@@ -462,12 +474,14 @@ module.exports = {
     isColonToken,
     isCommaToken,
     isCommentToken,
+    isDotToken,
     isKeywordToken,
     isNotClosingBraceToken: negate(isClosingBraceToken),
     isNotClosingBracketToken: negate(isClosingBracketToken),
     isNotClosingParenToken: negate(isClosingParenToken),
     isNotColonToken: negate(isColonToken),
     isNotCommaToken: negate(isCommaToken),
+    isNotDotToken: negate(isDotToken),
     isNotOpeningBraceToken: negate(isOpeningBraceToken),
     isNotOpeningBracketToken: negate(isOpeningBracketToken),
     isNotOpeningParenToken: negate(isOpeningParenToken),
@@ -988,7 +1002,18 @@ module.exports = {
      * '5'     // false
      */
     isDecimalInteger(node) {
-        return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/u.test(node.raw);
+        return node.type === "Literal" && typeof node.value === "number" &&
+            DECIMAL_INTEGER_PATTERN.test(node.raw);
+    },
+
+    /**
+     * Determines whether this token is a decimal integer numeric token.
+     * This is similar to isDecimalInteger(), but for tokens.
+     * @param {Token} token - The token to check.
+     * @returns {boolean} `true` if this token is a decimal integer.
+     */
+    isDecimalIntegerNumericToken(token) {
+        return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
     },
 
     /**
diff --git a/tools/update-readme.js b/tools/update-readme.js
index ce6c6e191..24fe3c01d 100644
--- a/tools/update-readme.js
+++ b/tools/update-readme.js
@@ -1,6 +1,6 @@
 /**
  * @fileoverview Script to update the README with team and sponsors.
- * Note that this requires eslint.github.io to be available in the same
+ * Note that this requires eslint/website to be available in the same
  * directory as the eslint repo.
  *
  *   node tools/update-readme.js
@@ -23,7 +23,7 @@ const ejs = require("ejs");
 //-----------------------------------------------------------------------------
 
 const README_FILE_PATH = path.resolve(__dirname, "../README.md");
-const WEBSITE_DATA_PATH = path.resolve(__dirname, "../../eslint.github.io/_data");
+const WEBSITE_DATA_PATH = path.resolve(__dirname, "../../website/_data");
 
 const team = JSON.parse(fs.readFileSync(path.join(WEBSITE_DATA_PATH, "team.json")));
 const allSponsors = JSON.parse(fs.readFileSync(path.join(WEBSITE_DATA_PATH, "sponsors.json")));


Test patch:
diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js
index de152f3ba..427181aff 100644
--- a/tests/lib/cli-engine/cli-engine.js
+++ b/tests/lib/cli-engine/cli-engine.js
@@ -3454,6 +3454,47 @@ describe("CLIEngine", () => {
                 assert.strictEqual(report.results[0].messages[0].message, "ok");
             });
         });
+
+        describe("glob pattern '[ab].js'", () => {
+            const root = getFixturePath("cli-engine/unmatched-glob");
+
+            it("should match '[ab].js' if existed.", () => {
+                CLIEngine = defineCLIEngineWithInMemoryFileSystem({
+                    cwd: () => root,
+                    files: {
+                        "a.js": "",
+                        "b.js": "",
+                        "ab.js": "",
+                        "[ab].js": "",
+                        ".eslintrc.yml": "root: true"
+                    }
+                }).CLIEngine;
+                engine = new CLIEngine();
+
+                const { results } = engine.executeOnFiles(["[ab].js"]);
+                const filenames = results.map(r => path.basename(r.filePath));
+
+                assert.deepStrictEqual(filenames, ["[ab].js"]);
+            });
+
+            it("should match 'a.js' and 'b.js' if '[ab].js' didn't existed.", () => {
+                CLIEngine = defineCLIEngineWithInMemoryFileSystem({
+                    cwd: () => root,
+                    files: {
+                        "a.js": "",
+                        "b.js": "",
+                        "ab.js": "",
+                        ".eslintrc.yml": "root: true"
+                    }
+                }).CLIEngine;
+                engine = new CLIEngine();
+
+                const { results } = engine.executeOnFiles(["[ab].js"]);
+                const filenames = results.map(r => path.basename(r.filePath));
+
+                assert.deepStrictEqual(filenames, ["a.js", "b.js"]);
+            });
+        });
     });
 
     describe("getConfigForFile", () => {
diff --git a/tests/lib/rules/arrow-body-style.js b/tests/lib/rules/arrow-body-style.js
index 2c4fa953a..4e755c2ca 100644
--- a/tests/lib/rules/arrow-body-style.js
+++ b/tests/lib/rules/arrow-body-style.js
@@ -162,6 +162,18 @@ ruleTester.run("arrow-body-style", rule, {
                 }
             ]
         },
+        {
+            code: "var foo = () => { return a, b }",
+            output: "var foo = () => (a, b)",
+            errors: [
+                {
+                    line: 1,
+                    column: 17,
+                    type: "ArrowFunctionExpression",
+                    messageId: "unexpectedSingleBlock"
+                }
+            ]
+        },
         {
             code: "var foo = () => { return };",
             output: null, // not fixed
diff --git a/tests/lib/rules/dot-location.js b/tests/lib/rules/dot-location.js
index d250b0e2c..3f6caea60 100644
--- a/tests/lib/rules/dot-location.js
+++ b/tests/lib/rules/dot-location.js
@@ -37,6 +37,105 @@ ruleTester.run("dot-location", rule, {
         {
             code: "(obj)\n.prop",
             options: ["property"]
+        },
+        {
+            code: "obj . prop",
+            options: ["object"]
+        },
+        {
+            code: "obj /* a */ . prop",
+            options: ["object"]
+        },
+        {
+            code: "obj . \nprop",
+            options: ["object"]
+        },
+        {
+            code: "obj . prop",
+            options: ["property"]
+        },
+        {
+            code: "obj . /* a */ prop",
+            options: ["property"]
+        },
+        {
+            code: "obj\n. prop",
+            options: ["property"]
+        },
+        {
+            code: "f(a\n).prop",
+            options: ["object"]
+        },
+        {
+            code: "`\n`.prop",
+            options: ["object"],
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "obj[prop]",
+            options: ["object"]
+        },
+        {
+            code: "obj\n[prop]",
+            options: ["object"]
+        },
+        {
+            code: "obj[\nprop]",
+            options: ["object"]
+        },
+        {
+            code: "obj\n[\nprop\n]",
+            options: ["object"]
+        },
+        {
+            code: "obj[prop]",
+            options: ["property"]
+        },
+        {
+            code: "obj\n[prop]",
+            options: ["property"]
+        },
+        {
+            code: "obj[\nprop]",
+            options: ["property"]
+        },
+        {
+            code: "obj\n[\nprop\n]",
+            options: ["property"]
+        },
+
+        // https://github.com/eslint/eslint/issues/11868 (also in invalid)
+        {
+            code: "(obj).prop",
+            options: ["object"]
+        },
+        {
+            code: "(obj).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "(obj\n).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "(\nobj\n).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "((obj\n)).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "(f(a)\n).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "((obj\n)\n).\nprop",
+            options: ["object"]
+        },
+        {
+            code: "(\na &&\nb()\n).toString()",
+            options: ["object"]
         }
     ],
     invalid: [
@@ -81,6 +180,81 @@ ruleTester.run("dot-location", rule, {
             output: "foo. /* a */ \n /* b */  /* c */ bar",
             options: ["object"],
             errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 2, column: 10 }]
+        },
+        {
+            code: "f(a\n)\n.prop",
+            output: "f(a\n).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "`\n`\n.prop",
+            output: "`\n`.\nprop",
+            options: ["object"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+
+        // https://github.com/eslint/eslint/issues/11868 (also in valid)
+        {
+            code: "(a\n)\n.prop",
+            output: "(a\n).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "(a\n)\n.\nprop",
+            output: "(a\n).\n\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "(f(a)\n)\n.prop",
+            output: "(f(a)\n).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "(f(a\n)\n)\n.prop",
+            output: "(f(a\n)\n).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 4, column: 1 }]
+        },
+        {
+            code: "((obj\n))\n.prop",
+            output: "((obj\n)).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "((obj\n)\n)\n.prop",
+            output: "((obj\n)\n).\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 4, column: 1 }]
+        },
+        {
+            code: "(a\n) /* a */ \n.prop",
+            output: "(a\n). /* a */ \nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 1 }]
+        },
+        {
+            code: "(a\n)\n/* a */\n.prop",
+            output: "(a\n).\n/* a */\nprop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 4, column: 1 }]
+        },
+        {
+            code: "(a\n)\n/* a */.prop",
+            output: "(a\n).\n/* a */prop",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 3, column: 8 }]
+        },
+        {
+            code: "(5)\n.toExponential()",
+            output: "(5).\ntoExponential()",
+            options: ["object"],
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 2, column: 1 }]
         }
     ]
 });
diff --git a/tests/lib/rules/utils/ast-utils.js b/tests/lib/rules/utils/ast-utils.js
index 57927a64d..678b7f274 100644
--- a/tests/lib/rules/utils/ast-utils.js
+++ b/tests/lib/rules/utils/ast-utils.js
@@ -629,7 +629,7 @@ describe("ast-utils", () => {
         });
     });
 
-    describe("isDecimalInteger", () => {
+    {
         const expectedResults = {
             5: true,
             0: true,
@@ -642,12 +642,22 @@ describe("ast-utils", () => {
             "'5'": false
         };
 
-        Object.keys(expectedResults).forEach(key => {
-            it(`should return ${expectedResults[key]} for ${key}`, () => {
-                assert.strictEqual(astUtils.isDecimalInteger(espree.parse(key).body[0].expression), expectedResults[key]);
+        describe("isDecimalInteger", () => {
+            Object.keys(expectedResults).forEach(key => {
+                it(`should return ${expectedResults[key]} for ${key}`, () => {
+                    assert.strictEqual(astUtils.isDecimalInteger(espree.parse(key).body[0].expression), expectedResults[key]);
+                });
             });
         });
-    });
+
+        describe("isDecimalIntegerNumericToken", () => {
+            Object.keys(expectedResults).forEach(key => {
+                it(`should return ${expectedResults[key]} for ${key}`, () => {
+                    assert.strictEqual(astUtils.isDecimalIntegerNumericToken(espree.tokenize(key)[0]), expectedResults[key]);
+                });
+            });
+        });
+    }
 
     describe("getFunctionNameWithKind", () => {
         const expectedResults = {
@@ -993,6 +1003,28 @@ describe("ast-utils", () => {
         });
     }
 
+    {
+        const code = "const obj = {foo: 1.5, bar: a.b};";
+        const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens;
+        const expected = [false, false, false, false, false, false, false, false, false, false, false, true, false, false, false];
+
+        describe("isDotToken", () => {
+            tokens.forEach((token, index) => {
+                it(`should return ${expected[index]} for '${token.value}'.`, () => {
+                    assert.strictEqual(astUtils.isDotToken(token), expected[index]);
+                });
+            });
+        });
+
+        describe("isNotDotToken", () => {
+            tokens.forEach((token, index) => {
+                it(`should return ${!expected[index]} for '${token.value}'.`, () => {
+                    assert.strictEqual(astUtils.isNotDotToken(token), !expected[index]);
+                });
+            });
+        });
+    }
+
     describe("isCommentToken", () => {
         const code = "const obj = /*block*/ {foo: 1, bar: 2}; //line";
         const ast = espree.parse(code, { ecmaVersion: 6, tokens: true, comment: true });