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

Fix: no-mixed-spaces-and-tabs reports multiline strings #12566

Merged
merged 2 commits into from Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 17 additions & 66 deletions lib/rules/no-mixed-spaces-and-tabs.js
Expand Up @@ -30,7 +30,6 @@ module.exports = {
const sourceCode = context.getSourceCode();

let smartTabs;
const ignoredLocs = [];

switch (context.options[0]) {
case true: // Support old syntax, maybe add deprecation warning here
Expand All @@ -41,72 +40,30 @@ module.exports = {
smartTabs = false;
}

/**
* Determines if a given line and column are before a location.
* @param {Location} loc The location object from an AST node.
* @param {int} line The line to check.
* @param {int} column The column to check.
* @returns {boolean} True if the line and column are before the location, false if not.
* @private
*/
function beforeLoc(loc, line, column) {
if (line < loc.start.line) {
return true;
}
return line === loc.start.line && column < loc.start.column;
}

/**
* Determines if a given line and column are after a location.
* @param {Location} loc The location object from an AST node.
* @param {int} line The line to check.
* @param {int} column The column to check.
* @returns {boolean} True if the line and column are after the location, false if not.
* @private
*/
function afterLoc(loc, line, column) {
if (line > loc.end.line) {
return true;
}
return line === loc.end.line && column > loc.end.column;
}

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------

return {

TemplateElement(node) {
ignoredLocs.push(node.loc);
},

"Program:exit"(node) {
const lines = sourceCode.lines,
comments = sourceCode.getAllComments(),
ignoredCommentLines = new Set();

// Add all lines except the first ones.
comments.forEach(comment => {
kaicataldo marked this conversation as resolved.
Show resolved Hide resolved
for (let i = comment.loc.start.line + 1; i <= comment.loc.end.line; i++) {
ignoredCommentLines.add(i);
}
});

/*
* At least one space followed by a tab
* or the reverse before non-tab/-space
* characters begin.
*/
let regex = /^(?=[\t ]*(\t | \t))/u;
const lines = sourceCode.lines,
comments = sourceCode.getAllComments();

comments.forEach(comment => {
ignoredLocs.push(comment.loc);
});

ignoredLocs.sort((first, second) => {
if (beforeLoc(first, second.start.line, second.start.column)) {
return 1;
}

if (beforeLoc(second, first.start.line, second.start.column)) {
return -1;
}

return 0;
});

if (smartTabs) {

Expand All @@ -122,25 +79,19 @@ module.exports = {

if (match) {
const lineNumber = i + 1,
column = match.index + 1;
column = match.index + 1,
loc = { line: lineNumber, column };

for (let j = 0; j < ignoredLocs.length; j++) {
if (beforeLoc(ignoredLocs[j], lineNumber, column)) {
continue;
}
if (afterLoc(ignoredLocs[j], lineNumber, column)) {
continue;
}
if (!ignoredCommentLines.has(lineNumber)) {
const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc));

return;
if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) {
context.report({ node, loc, message: "Mixed spaces and tabs." });
}
}

context.report({ node, loc: { line: lineNumber, column }, message: "Mixed spaces and tabs." });
}
});
}

};

}
};
78 changes: 78 additions & 0 deletions tests/lib/rules/no-mixed-spaces-and-tabs.js
Expand Up @@ -25,6 +25,12 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, {
"\t/*\n\t * Hello\n\t */",
"// foo\n\t/**\n\t * Hello\n\t */",
"/*\n\n \t \n*/",
"/*\t */ //",
"/*\n \t*/ //",
"/*\n\t *//*\n \t*/",
"// \t",
"/*\n*/\t ",
"/* \t\n\t \n \t\n\t */ \t",
{
code: "\tvar x = 5,\n\t y = 2;",
options: [true]
Expand Down Expand Up @@ -69,6 +75,8 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, {
code: "`foo${ 5 }\t `;",
env: { es6: true }
},
"' \t\\\n\t multiline string';",
"'\t \\\n \tmultiline string';",
{
code: "\tvar x = 5,\n\t y = 2;",
options: ["smart-tabs"]
Expand Down Expand Up @@ -96,6 +104,56 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, {
}
]
},
{
code: " \t/* comment */",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 1
}
]
},
{
code: "\t // comment",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 1
}
]
},
{
code: "\t var a /* comment */ = 1;",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 1
}
]
},
{
code: " \tvar b = 1; // comment",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 1
}
]
},
{
code: "/**/\n \t/*\n \t*/",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 2
}
]
},
{
code: "\t var x = 5, y = 2, z = 5;\n\n\t \tvar j =\t x + y;\nz *= j;",
errors: [
Expand Down Expand Up @@ -157,6 +215,26 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, {
column: 2
}
]
},
{
code: " \t'';",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 1
}
]
},
{
code: "''\n\t ",
errors: [
{
message: "Mixed spaces and tabs.",
type: "Program",
line: 2
}
]
}
]
});