eslint-plugin-sonarjs
Advanced tools
Comparing version 0.12.0 to 0.13.0
@@ -30,5 +30,7 @@ "use strict"; | ||
fixCollectionSizeCheck: 'Fix this expression; {{propertyName}} of "{{objectName}}" is always greater or equal to zero.', | ||
suggestFixedSizeCheck: 'Use "{{operator}}" for {{operation}} check', | ||
}, | ||
schema: [], | ||
type: 'problem', | ||
hasSuggestions: true, | ||
docs: { | ||
@@ -61,2 +63,3 @@ description: 'Collection sizes and array length comparisons should make sense', | ||
node, | ||
suggest: getSuggestion(expr, property.name, context), | ||
}); | ||
@@ -78,3 +81,20 @@ } | ||
} | ||
function getSuggestion(expr, operation, context) { | ||
const { left, operator } = expr; | ||
const operatorToken = context | ||
.getSourceCode() | ||
.getTokenAfter(left, token => token.value === operator); | ||
const fixedOperator = operator === '<' ? '==' : '>'; | ||
return [ | ||
{ | ||
messageId: 'suggestFixedSizeCheck', | ||
data: { | ||
operation, | ||
operator: fixedOperator, | ||
}, | ||
fix: fixer => fixer.replaceText(operatorToken, fixedOperator), | ||
}, | ||
]; | ||
} | ||
module.exports = rule; | ||
//# sourceMappingURL=no-collection-size-mischeck.js.map |
@@ -28,5 +28,7 @@ "use strict"; | ||
removeRedundantJump: 'Remove this redundant jump.', | ||
suggestJumpRemoval: 'Remove this redundant jump', | ||
}, | ||
schema: [], | ||
type: 'suggestion', | ||
hasSuggestions: true, | ||
docs: { | ||
@@ -44,5 +46,15 @@ description: 'Jump statements should not be redundant', | ||
if (block.body[block.body.length - 1] === node && block.body.length > 1) { | ||
const previousComments = context.getSourceCode().getCommentsBefore(node); | ||
const previousToken = previousComments.length === 0 | ||
? context.getSourceCode().getTokenBefore(node) | ||
: previousComments[previousComments.length - 1]; | ||
context.report({ | ||
messageId: 'removeRedundantJump', | ||
node, | ||
suggest: [ | ||
{ | ||
messageId: 'suggestJumpRemoval', | ||
fix: fixer => fixer.removeRange([previousToken.range[1], node.range[1]]), | ||
}, | ||
], | ||
}); | ||
@@ -49,0 +61,0 @@ } |
@@ -30,4 +30,7 @@ "use strict"; | ||
sonarRuntime: '{{sonarRuntimeData}}', | ||
suggestAddingElse: 'Add "else" keyword', | ||
suggestAddingNewline: 'Move this "if" to a new line', | ||
}, | ||
type: 'problem', | ||
hasSuggestions: true, | ||
docs: { | ||
@@ -61,2 +64,12 @@ description: 'Conditionals should start on new lines', | ||
loc: followingIfToken.loc, | ||
suggest: [ | ||
{ | ||
messageId: 'suggestAddingElse', | ||
fix: fixer => fixer.insertTextBefore(followingIfToken, 'else '), | ||
}, | ||
{ | ||
messageId: 'suggestAddingNewline', | ||
fix: fixer => fixer.replaceTextRange([precedingIf.range[1], followingIf.range[0]], '\n' + ' '.repeat(precedingIf.loc.start.column)), | ||
}, | ||
], | ||
}, [(0, locations_1.issueLocation)(precedingIfLastToken.loc)], message); | ||
@@ -63,0 +76,0 @@ } |
@@ -27,5 +27,7 @@ "use strict"; | ||
useExistingOperator: 'Was "{{operator}}=" meant instead?', | ||
suggestExistingOperator: 'Replace with "{{operator}}" operator', | ||
}, | ||
schema: [], | ||
type: 'problem', | ||
hasSuggestions: true, | ||
docs: { | ||
@@ -53,2 +55,3 @@ description: 'Non-existent operators "=+", "=-" and "=!" should not be used', | ||
function checkOperator(context, unaryNode) { | ||
var _a; | ||
if (unaryNode && | ||
@@ -66,2 +69,17 @@ unaryNode.type === 'UnaryExpression' && | ||
!areAdjacent(unaryOperatorToken, expressionFirstToken)) { | ||
const suggest = []; | ||
if (((_a = unaryNode.parent) === null || _a === void 0 ? void 0 : _a.type) === 'AssignmentExpression') { | ||
const range = [ | ||
assignmentOperatorToken.range[0], | ||
unaryOperatorToken.range[1], | ||
]; | ||
const invertedOperators = unaryOperatorToken.value + assignmentOperatorToken.value; | ||
suggest.push({ | ||
messageId: 'suggestExistingOperator', | ||
data: { | ||
operator: invertedOperators, | ||
}, | ||
fix: fixer => fixer.replaceTextRange(range, invertedOperators), | ||
}); | ||
} | ||
context.report({ | ||
@@ -73,2 +91,3 @@ messageId: 'useExistingOperator', | ||
loc: { start: assignmentOperatorToken.loc.start, end: unaryOperatorToken.loc.end }, | ||
suggest, | ||
}); | ||
@@ -75,0 +94,0 @@ } |
@@ -8,5 +8,9 @@ "use strict"; | ||
replaceIfThenElseByReturn: 'Replace this if-then-else flow by a single return statement.', | ||
suggest: 'Replace with single return statement', | ||
suggestCast: 'Replace with single return statement using "!!" cast', | ||
suggestBoolean: 'Replace with single return statement without cast (condition should be boolean!)', | ||
}, | ||
schema: [], | ||
type: 'suggestion', | ||
hasSuggestions: true, | ||
docs: { | ||
@@ -29,2 +33,3 @@ description: 'Return of boolean expressions should not be wrapped into an "if-then-else" statement', | ||
node, | ||
suggest: getSuggestion(node), | ||
}); | ||
@@ -58,2 +63,42 @@ } | ||
} | ||
function getSuggestion(ifStmt) { | ||
const getFix = (condition) => { | ||
return (fixer) => { | ||
const singleReturn = `return ${condition};`; | ||
if (ifStmt.alternate) { | ||
return fixer.replaceText(ifStmt, singleReturn); | ||
} | ||
else { | ||
const parent = ifStmt.parent; | ||
const ifStmtIndex = parent.body.findIndex(stmt => stmt === ifStmt); | ||
const returnStmt = parent.body[ifStmtIndex + 1]; | ||
const range = [ifStmt.range[0], returnStmt.range[1]]; | ||
return fixer.replaceTextRange(range, singleReturn); | ||
} | ||
}; | ||
}; | ||
const shouldNegate = isReturningFalse(ifStmt.consequent); | ||
const shouldCast = !isBooleanExpression(ifStmt.test); | ||
const testText = context.getSourceCode().getText(ifStmt.test); | ||
if (shouldNegate) { | ||
return [{ messageId: 'suggest', fix: getFix(`!(${testText})`) }]; | ||
} | ||
else if (!shouldCast) { | ||
return [{ messageId: 'suggest', fix: getFix(testText) }]; | ||
} | ||
else { | ||
return [ | ||
{ messageId: 'suggestCast', fix: getFix(`!!(${testText})`) }, | ||
{ messageId: 'suggestBoolean', fix: getFix(testText) }, | ||
]; | ||
} | ||
} | ||
function isReturningFalse(stmt) { | ||
const returnStmt = (stmt.type === 'BlockStatement' ? stmt.body[0] : stmt); | ||
return returnStmt.argument.value === false; | ||
} | ||
function isBooleanExpression(expr) { | ||
return ((expr.type === 'UnaryExpression' || expr.type === 'BinaryExpression') && | ||
['!', '==', '===', '!=', '!==', '<', '<=', '>', '>=', 'in', 'instanceof'].includes(expr.operator)); | ||
} | ||
}, | ||
@@ -60,0 +105,0 @@ }; |
{ | ||
"name": "eslint-plugin-sonarjs", | ||
"version": "0.12.0", | ||
"version": "0.13.0", | ||
"description": "SonarJS rules for ESLint", | ||
@@ -33,3 +33,3 @@ "main": "lib/index.js", | ||
"peerDependencies": { | ||
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0|| ^8.0.0" | ||
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" | ||
}, | ||
@@ -49,3 +49,3 @@ "devDependencies": { | ||
"eslint-plugin-notice": "0.6.7", | ||
"eslint-plugin-sonarjs": "0.10.0", | ||
"eslint-plugin-sonarjs": "0.12.0", | ||
"jest": "27.3.1", | ||
@@ -55,3 +55,3 @@ "jest-sonar-reporter": "2.0.0", | ||
"lodash": "4.17.21", | ||
"minimist": "1.2.3", | ||
"minimist": "1.2.6", | ||
"prettier": "2.3.0", | ||
@@ -58,0 +58,0 @@ "rimraf": "2.6.2", |
@@ -20,3 +20,3 @@ # eslint-plugin-sonarjs [![npm version](https://badge.fury.io/js/eslint-plugin-sonarjs.svg)](https://badge.fury.io/js/eslint-plugin-sonarjs) [![Build Status](https://api.cirrus-ci.com/github/SonarSource/eslint-plugin-sonarjs.svg?branch=master)](https://cirrus-ci.com/github/SonarSource/eslint-plugin-sonarjs) [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=eslint-plugin-sonarjs&metric=alert_status)](https://sonarcloud.io/dashboard?id=eslint-plugin-sonarjs) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=eslint-plugin-sonarjs&metric=coverage)](https://sonarcloud.io/dashboard?id=eslint-plugin-sonarjs) | ||
* The output of functions that don't return anything should not be used ([`no-use-of-empty-return-value`]) | ||
* Non-existent operators '=+', '=-' and '=!' should not be used ([`non-existent-operator`]) | ||
* Non-existent operators '=+', '=-' and '=!' should not be used ([`non-existent-operator`]) (:wrench: *fixable*) | ||
@@ -31,3 +31,3 @@ ### Code Smell Detection :pig: | ||
* Collapsible "if" statements should be merged ([`no-collapsible-if`]) | ||
* Collection sizes and array length comparisons should make sense ([`no-collection-size-mischeck`]) (*uses-types*) | ||
* Collection sizes and array length comparisons should make sense ([`no-collection-size-mischeck`]) (:wrench: *fixable*, *uses-types*) | ||
* String literals should not be duplicated ([`no-duplicate-string`]) | ||
@@ -41,4 +41,4 @@ * Two branches in a conditional structure should not have exactly the same implementation ([`no-duplicated-branches`]) | ||
* Boolean literals should not be redundant ([`no-redundant-boolean`]) | ||
* Jump statements should not be redundant ([`no-redundant-jump`]) | ||
* Conditionals should start on new lines ([`no-same-line-conditional`]) | ||
* Jump statements should not be redundant ([`no-redundant-jump`]) (:wrench: *fixable*) | ||
* Conditionals should start on new lines ([`no-same-line-conditional`]) (:wrench: *fixable*) | ||
* "switch" statements should have at least 3 "case" clauses ([`no-small-switch`]) | ||
@@ -49,3 +49,3 @@ * Collection and array contents should be used ([`no-unused-collection`]) | ||
* Object literal syntax should be used ([`prefer-object-literal`]) | ||
* Return of boolean expressions should not be wrapped into an "if-then-else" statement ([`prefer-single-boolean-return`]) | ||
* Return of boolean expressions should not be wrapped into an "if-then-else" statement ([`prefer-single-boolean-return`]) (:wrench: *fixable*) | ||
* A "while" loop should be used instead of a "for" loop ([`prefer-while`]) (:wrench: *fixable*) | ||
@@ -133,4 +133,11 @@ | ||
This plugin provides only `recommended` configuration. Almost all rules are activated in this profile with a few exceptions (check `disabled` tag in the rules list). `recommended` configuration activates rules with `error` severity. | ||
## ESLint and Sonar | ||
This plugin exposes to ESLint users a subset of JS/TS rules from Sonar-* products (aka [SonarJS](https://github.com/SonarSource/SonarJS)). We extracted the rules which are not available in ESLint core or other ESLint plugins to be beneficial for ESLint community. | ||
If you are a [SonarQube](https://www.sonarqube.org) or [SonarCloud](https://sonarcloud.io) user, to lint your code locally, we suggest to use [SonarLint](https://www.sonarlint.org) IDE extension (available for VSCode, JetBrains IDEs and Eclipse). You can connect SonarLint to your SonarQube/SonarCloud project to synchronize rules configuration, issue statuses, etc. | ||
## Contributing | ||
You want to participate in the development of the project? Have a look at our [contributing](./docs/CONTRIBUTING.md) guide! |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
307693
4478
139