@fimbul/mimir
Advanced tools
Comparing version 0.6.0-dev.20180318 to 0.6.0-dev.20180320
{ | ||
"name": "@fimbul/mimir", | ||
"version": "0.6.0-dev.20180318", | ||
"version": "0.6.0-dev.20180320", | ||
"description": "Core rules of the Fimbullinter project", | ||
@@ -5,0 +5,0 @@ "main": "recommended.yaml", |
@@ -40,2 +40,3 @@ # Mímir | ||
`no-useless-initializer` | Detects unnecessary initialization with `undefined`. | TSLint's rule `no-unnecessary-initializer` doesn't fix all parameter initializers and gives false positives for destructuring. | ||
`no-useless-jump-label` | Detects `continue label;` and `break label;` where the label is not necessary. | There's no similar TSLint rule. | ||
`no-useless-predicate` | Detects redundant conditions that are either always true or always false. *requires type information* | Combination of TSLint's `strict-type-predicates`, `typeof-compare` and parts of `strict-boolean-expressions`. | ||
@@ -42,0 +43,0 @@ `prefer-const` | Prefer `const` for variables that are never reassigned. Use option `{destructuring: "any"}` if you want to see failures for each identifier of a destructuring, even if not all of them can be constants. The default is `{destructuring: "all"}`. | TSLint's `prefer-const` rule gives some false positives for merged declarations and variables used in before being declared which results in a compiler error after fixing. |
@@ -15,12 +15,10 @@ "use strict"; | ||
const { node } = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = this.context.getWrappedAst()), match.index); | ||
if (match.index !== node.getStart(this.sourceFile)) | ||
continue; | ||
if (tsutils_1.isAwaitExpression(node)) { | ||
if (!this.isPromiseLike(node.expression)) | ||
if (node.expression.pos === re.lastIndex && !this.isPromiseLike(node.expression)) | ||
this.addFailure(match.index, node.end, "Unnecessary 'await' of a non-Promise value.", ymir_1.Replacement.delete(match.index, node.expression.getStart(this.sourceFile))); | ||
} | ||
else if (node.kind === ts.SyntaxKind.AwaitKeyword) { | ||
else if (node.kind === ts.SyntaxKind.AwaitKeyword && node.end === re.lastIndex) { | ||
const parent = node.parent; | ||
if (tsutils_1.isForOfStatement(parent) && !this.isAsyncIterable(parent.expression)) { | ||
const start = parent.getStart(this.sourceFile); | ||
const start = node.pos - 'for'.length; | ||
this.addFailure(start, parent.statement.pos, "Unnecessary 'for await' of a non-AsyncIterable value.", ymir_1.Replacement.delete(start + 'for'.length, re.lastIndex)); | ||
@@ -27,0 +25,0 @@ } |
@@ -5,2 +5,3 @@ "use strict"; | ||
const ts = require("typescript"); | ||
const tsutils_1 = require("tsutils"); | ||
class Rule extends ymir_1.AbstractRule { | ||
@@ -11,5 +12,12 @@ static supports(sourceFile) { | ||
apply() { | ||
for (const node of this.context.getFlatAst()) | ||
if (node.kind === ts.SyntaxKind.NewExpression && this.sourceFile.text[node.end - 1] !== ')') | ||
let wrappedAst; | ||
const { text } = this.sourceFile; | ||
const re = /\bnew\b/g; | ||
for (let match = re.exec(text); match !== null; match = re.exec(text)) { | ||
const { node } = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = this.context.getWrappedAst()), match.index); | ||
if (node.kind === ts.SyntaxKind.NewExpression && | ||
text[node.end - 1] !== ')' && | ||
re.lastIndex === node.expression.pos) | ||
this.addFailure(node.end, node.end, 'Expected parentheses on constructor call.', ymir_1.Replacement.append(node.end, '()')); | ||
} | ||
} | ||
@@ -16,0 +24,0 @@ } |
@@ -6,2 +6,3 @@ "use strict"; | ||
const tsutils_1 = require("tsutils"); | ||
const utils_1 = require("../utils"); | ||
class Rule extends ymir_1.AbstractRule { | ||
@@ -12,8 +13,7 @@ static supports(sourceFile) { | ||
apply() { | ||
for (const node of this.context.getFlatAst()) | ||
if (node.kind === ts.SyntaxKind.CaseBlock) | ||
for (const clause of node.clauses) | ||
for (const statement of clause.statements) | ||
if (isForbiddenDeclaration(statement)) | ||
this.addFailureAtNode(statement, 'Unexpected lexical declaration in case block.'); | ||
for (const { caseBlock: { clauses } } of utils_1.switchStatements(this.context)) | ||
for (const clause of clauses) | ||
for (const statement of clause.statements) | ||
if (isForbiddenDeclaration(statement)) | ||
this.addFailureAtNode(statement, 'Unexpected lexical declaration in case block.'); | ||
} | ||
@@ -20,0 +20,0 @@ } |
@@ -6,3 +6,2 @@ import { AbstractRule } from '@fimbul/ymir'; | ||
apply(): void; | ||
private checkFallthrough(clauses); | ||
} |
@@ -6,2 +6,3 @@ "use strict"; | ||
const tsutils_1 = require("tsutils"); | ||
const utils_1 = require("../utils"); | ||
class Rule extends ymir_1.AbstractRule { | ||
@@ -12,18 +13,11 @@ static supports(sourceFile) { | ||
apply() { | ||
const re = /\bswitch\s*?[\r\n(/]/g; | ||
let wrappedAst; | ||
for (let match = re.exec(this.sourceFile.text); match !== null; match = re.exec(this.sourceFile.text)) { | ||
const { node } = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = this.context.getWrappedAst()), match.index); | ||
if (tsutils_1.isSwitchStatement(node) && node.getStart(this.sourceFile) === match.index) | ||
this.checkFallthrough(node.caseBlock.clauses); | ||
} | ||
} | ||
checkFallthrough(clauses) { | ||
for (let i = 1; i < clauses.length; ++i) { | ||
if (clauses[i - 1].statements.length !== 0 && | ||
!ts.forEachLeadingCommentRange(this.sourceFile.text, clauses[i].pos, isFallthroughComment, this.sourceFile.text) && | ||
!tsutils_1.endsControlFlow(clauses[i - 1])) { | ||
const kind = clauses[i].kind === ts.SyntaxKind.CaseClause ? 'case' : 'default'; | ||
const start = clauses[i].getStart(this.sourceFile); | ||
this.addFailure(start, start + kind.length, `Missing 'break' before '${kind}'.`); | ||
for (const { caseBlock: { clauses } } of utils_1.switchStatements(this.context)) { | ||
for (let i = 1; i < clauses.length; ++i) { | ||
if (clauses[i - 1].statements.length !== 0 && | ||
!ts.forEachLeadingCommentRange(this.sourceFile.text, clauses[i].pos, isFallthroughComment, this.sourceFile.text) && | ||
!tsutils_1.endsControlFlow(clauses[i - 1])) { | ||
const kind = clauses[i].kind === ts.SyntaxKind.CaseClause ? 'case' : 'default'; | ||
const start = clauses[i].getStart(this.sourceFile); | ||
this.addFailure(start, start + kind.length, `Missing 'break' before '${kind}'.`); | ||
} | ||
} | ||
@@ -30,0 +24,0 @@ } |
@@ -5,7 +5,6 @@ import { TypedRule } from '@fimbul/ymir'; | ||
static supports(sourceFile: ts.SourceFile): boolean; | ||
private inTryCatch; | ||
private reported; | ||
apply(): void; | ||
private iterate(wrap, end, inTryCatch); | ||
private visitNode(wrap); | ||
private visitStatement(node); | ||
private checkReturnExpression(node); | ||
} |
@@ -10,3 +10,3 @@ "use strict"; | ||
super(...arguments); | ||
this.inTryCatch = false; | ||
this.reported = []; | ||
} | ||
@@ -17,40 +17,44 @@ static supports(sourceFile) { | ||
apply() { | ||
return this.iterate(this.context.getWrappedAst().next, undefined, false); | ||
} | ||
iterate(wrap, end, inTryCatch) { | ||
do { | ||
if (!utils_1.isAsyncFunction(wrap.node)) { | ||
wrap = wrap.next; | ||
continue; | ||
const re = /\btry\s*[/{]/g; | ||
let wrappedAst; | ||
for (let match = re.exec(this.sourceFile.text); match !== null; match = re.exec(this.sourceFile.text)) { | ||
const { node } = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = this.context.getWrappedAst()), match.index); | ||
if (tsutils_1.isTryStatement(node) && | ||
match.index === node.tryBlock.pos - 'try'.length && | ||
!this.reported.includes(node) && | ||
isInAsyncFunction(node)) { | ||
node.tryBlock.statements.forEach(this.visitStatement, this); | ||
if (node.catchClause !== undefined && node.finallyBlock !== undefined) | ||
node.catchClause.block.statements.forEach(this.visitStatement, this); | ||
} | ||
this.inTryCatch = false; | ||
wrap.children.forEach(this.visitNode, this); | ||
this.inTryCatch = inTryCatch; | ||
wrap = wrap.skip; | ||
} while (wrap !== end); | ||
} | ||
} | ||
visitNode(wrap) { | ||
if (this.inTryCatch) { | ||
if (tsutils_1.isReturnStatement(wrap.node)) { | ||
if (wrap.node.expression === undefined) | ||
return; | ||
this.checkReturnExpression(wrap.node.expression); | ||
return this.iterate(wrap.next, wrap.skip, true); | ||
} | ||
visitStatement(node) { | ||
if (tsutils_1.isReturnStatement(node)) { | ||
if (node.expression !== undefined) | ||
return this.checkReturnExpression(node.expression); | ||
} | ||
else if (tsutils_1.isTryStatement(wrap.node)) { | ||
this.inTryCatch = true; | ||
wrap.children[0].children.forEach(this.visitNode, this); | ||
if (wrap.node.catchClause !== undefined) { | ||
this.inTryCatch = wrap.node.finallyBlock !== undefined; | ||
wrap.children[1].children.forEach(this.visitNode, this); | ||
} | ||
this.inTryCatch = false; | ||
if (wrap.node.finallyBlock !== undefined) | ||
wrap.children[wrap.children.length - 1].children.forEach(this.visitNode, this); | ||
return; | ||
else if (tsutils_1.isIfStatement(node)) { | ||
if (node.elseStatement !== undefined) | ||
this.visitStatement(node.elseStatement); | ||
return this.visitStatement(node.thenStatement); | ||
} | ||
if (tsutils_1.isFunctionScopeBoundary(wrap.node)) | ||
return this.iterate(wrap, wrap.skip, this.inTryCatch); | ||
return wrap.children.forEach(this.visitNode, this); | ||
else if (tsutils_1.isIterationStatement(node) || tsutils_1.isLabeledStatement(node)) { | ||
return this.visitStatement(node.statement); | ||
} | ||
else if (tsutils_1.isSwitchStatement(node)) { | ||
for (const clause of node.caseBlock.clauses) | ||
clause.statements.forEach(this.visitStatement, this); | ||
} | ||
else if (tsutils_1.isBlock(node)) { | ||
return node.statements.forEach(this.visitStatement, this); | ||
} | ||
else if (tsutils_1.isTryStatement(node)) { | ||
this.reported.push(node); | ||
if (node.catchClause !== undefined) | ||
node.catchClause.block.statements.forEach(this.visitStatement, this); | ||
if (node.finallyBlock !== undefined) | ||
node.finallyBlock.statements.forEach(this.visitStatement, this); | ||
return node.tryBlock.statements.forEach(this.visitStatement, this); | ||
} | ||
} | ||
@@ -74,2 +78,10 @@ checkReturnExpression(node) { | ||
exports.Rule = Rule; | ||
function isInAsyncFunction(node) { | ||
do { | ||
node = node.parent; | ||
if (node.kind === ts.SyntaxKind.SourceFile) | ||
return false; | ||
} while (!tsutils_1.isFunctionScopeBoundary(node)); | ||
return utils_1.isAsyncFunction(node); | ||
} | ||
function needsParens(node) { | ||
@@ -76,0 +88,0 @@ switch (node.kind) { |
import * as ts from 'typescript'; | ||
import { RuleContext } from '@fimbul/ymir'; | ||
export declare function isStrictNullChecksEnabled(options: ts.CompilerOptions): boolean; | ||
export declare function isStrictPropertyInitializationEnabled(options: ts.CompilerOptions): boolean; | ||
export declare function switchStatements(context: RuleContext): IterableIterator<ts.SwitchStatement>; | ||
export declare function isAsyncFunction(node: ts.Node): node is ts.FunctionLikeDeclaration; |
@@ -15,2 +15,13 @@ "use strict"; | ||
exports.isStrictPropertyInitializationEnabled = isStrictPropertyInitializationEnabled; | ||
function* switchStatements(context) { | ||
const { text } = context.sourceFile; | ||
const re = /\bswitch\s*[(/]/g; | ||
let wrappedAst; | ||
for (let match = re.exec(text); match !== null; match = re.exec(text)) { | ||
const { node } = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = context.getWrappedAst()), match.index); | ||
if (node.kind === ts.SyntaxKind.SwitchStatement && node.getStart(context.sourceFile) === match.index) | ||
yield node; | ||
} | ||
} | ||
exports.switchStatements = switchStatements; | ||
function isAsyncFunction(node) { | ||
@@ -17,0 +28,0 @@ switch (node.kind) { |
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
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
222521
99
2501
65