@fimbul/mimir
Advanced tools
Comparing version 0.8.0-dev.20180411 to 0.8.0-dev.20180415
{ | ||
"name": "@fimbul/mimir", | ||
"version": "0.8.0-dev.20180411", | ||
"version": "0.8.0-dev.20180415", | ||
"description": "Core rules of the Fimbullinter project", | ||
@@ -5,0 +5,0 @@ "main": "recommended.yaml", |
import { TypedRule } from '@fimbul/ymir'; | ||
export declare class Rule extends TypedRule { | ||
apply(): void; | ||
private iterate(wrap, end); | ||
private visitNode(wrap); | ||
private visitStatement(node); | ||
} |
@@ -7,25 +7,24 @@ "use strict"; | ||
const utils_1 = require("../utils"); | ||
const ts = require("typescript"); | ||
let Rule = class Rule extends ymir_1.TypedRule { | ||
apply() { | ||
return this.iterate(this.context.getWrappedAst().next, undefined); | ||
} | ||
iterate(wrap, end) { | ||
do { | ||
if (!utils_1.isAsyncFunction(wrap.node)) { | ||
wrap = wrap.next; | ||
const re = /\basync\b/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 (node.kind !== ts.SyntaxKind.AsyncKeyword || node.end !== re.lastIndex) | ||
continue; | ||
} | ||
wrap.children.forEach(this.visitNode, this); | ||
wrap = wrap.skip; | ||
} while (wrap !== end); | ||
const parent = node.parent; | ||
if (utils_1.isAsyncFunction(parent)) | ||
parent.body.statements.forEach(this.visitStatement, this); | ||
} | ||
} | ||
visitNode(wrap) { | ||
if (tsutils_1.isExpressionStatement(wrap.node)) { | ||
if (tsutils_1.isCallExpression(wrap.node.expression) && tsutils_1.isThenableType(this.checker, wrap.node.expression)) | ||
this.addFailureAtNode(wrap.node, "Return value of async function call was discarded. Did you mean to 'await' its result?"); | ||
return this.iterate(wrap.next, wrap.skip); | ||
visitStatement(node) { | ||
if (tsutils_1.isExpressionStatement(node)) { | ||
if (tsutils_1.isCallExpression(node.expression) && tsutils_1.isThenableType(this.checker, node.expression)) | ||
this.addFailureAtNode(node, "Return value of async function call was discarded. Did you mean to 'await' its result?"); | ||
return; | ||
} | ||
if (tsutils_1.isFunctionScopeBoundary(wrap.node)) | ||
return this.iterate(wrap, wrap.skip); | ||
return wrap.children.forEach(this.visitNode, this); | ||
for (const statement of utils_1.childStatements(node)) | ||
this.visitStatement(statement); | ||
} | ||
@@ -32,0 +31,0 @@ }; |
import { AbstractRule } from '@fimbul/ymir'; | ||
export declare class Rule extends AbstractRule { | ||
private labels; | ||
apply(): void; | ||
private iterate(wrap, end); | ||
private visitNode(wrap); | ||
private fail(statement); | ||
} |
@@ -7,53 +7,15 @@ "use strict"; | ||
const ymir_1 = require("@fimbul/ymir"); | ||
const utils_1 = require("../utils"); | ||
let Rule = class Rule extends ymir_1.AbstractRule { | ||
constructor() { | ||
super(...arguments); | ||
this.labels = []; | ||
} | ||
apply() { | ||
return this.iterate(this.context.getWrappedAst().next, undefined); | ||
} | ||
iterate(wrap, end) { | ||
do { | ||
if (wrap.kind === ts.SyntaxKind.LabeledStatement) { | ||
this.visitNode(wrap); | ||
wrap = wrap.skip; | ||
for (const node of this.context.getFlatAst()) { | ||
if (node.kind === ts.SyntaxKind.LabeledStatement) { | ||
const { label, statement } = node; | ||
if (!usesLabel(statement, label.text)) { | ||
const start = label.getStart(this.sourceFile); | ||
this.addFailure(start, label.end, `Unused label '${label.text}'.`, ymir_1.Replacement.delete(start, statement.getStart(this.sourceFile))); | ||
} | ||
} | ||
else { | ||
wrap = wrap.next; | ||
} | ||
} while (wrap !== end); | ||
} | ||
visitNode(wrap) { | ||
const { node } = wrap; | ||
if (tsutils_1.isLabeledStatement(node)) { | ||
this.labels.unshift({ node, name: node.label.text, used: false }); | ||
this.visitNode(wrap.children[1]); | ||
const label = this.labels.shift(); | ||
if (!label.used) | ||
this.fail(label.node); | ||
return; | ||
} | ||
if (tsutils_1.isBreakOrContinueStatement(node)) { | ||
if (node.label !== undefined) { | ||
const name = node.label.text; | ||
const label = this.labels.find((l) => l.name === name); | ||
if (label !== undefined) | ||
label.used = true; | ||
} | ||
return; | ||
} | ||
if (tsutils_1.isFunctionScopeBoundary(node)) { | ||
const saved = this.labels; | ||
this.labels = []; | ||
this.iterate(wrap.next, wrap.skip); | ||
this.labels = saved; | ||
return; | ||
} | ||
return wrap.children.forEach(this.visitNode, this); | ||
} | ||
fail(statement) { | ||
const start = statement.label.getStart(this.sourceFile); | ||
this.addFailure(start, statement.label.end, `Unused label '${statement.label.text}'.`, ymir_1.Replacement.delete(start, statement.statement.getStart(this.sourceFile))); | ||
} | ||
}; | ||
@@ -64,2 +26,12 @@ Rule = tslib_1.__decorate([ | ||
exports.Rule = Rule; | ||
function usesLabel(node, label) { | ||
if (tsutils_1.isBreakOrContinueStatement(node)) | ||
return node.label !== undefined && node.label.text === label; | ||
if (tsutils_1.isLabeledStatement(node)) | ||
return node.label.text !== label && usesLabel(node.statement, label); | ||
for (const statement of utils_1.childStatements(node)) | ||
if (usesLabel(statement, label)) | ||
return true; | ||
return false; | ||
} | ||
//# sourceMappingURL=no-unused-label.js.map |
@@ -28,14 +28,11 @@ "use strict"; | ||
function removeUselessSpread(node) { | ||
const list = tsutils_1.isArrayLiteralExpression(node.expression) | ||
? node.expression.elements | ||
: node.expression.properties; | ||
if (tsutils_1.isSpreadElement(node) && tsutils_1.isArrayLiteralExpression(node.expression) && list.length === 0) | ||
if (node.kind !== ts.SyntaxKind.SpreadElement) | ||
return; | ||
const list = node.expression.elements; | ||
if (list.length === 0) | ||
return removeUselessSpreadOfEmptyArray(node); | ||
const replacements = [ | ||
return [ | ||
ymir_1.Replacement.delete(node.getStart(), node.expression.getStart() + 1), | ||
ymir_1.Replacement.delete(node.expression.end - 1, node.expression.end), | ||
ymir_1.Replacement.delete(list[list.length - 1].end, node.expression.end), | ||
]; | ||
if (list.hasTrailingComma) | ||
replacements.push(ymir_1.Replacement.delete(list.end - 1, list.end)); | ||
return replacements; | ||
} | ||
@@ -42,0 +39,0 @@ function removeUselessSpreadOfEmptyArray(node) { |
@@ -11,3 +11,3 @@ "use strict"; | ||
super(...arguments); | ||
this.reported = []; | ||
this.reported = new Set(); | ||
} | ||
@@ -21,3 +21,3 @@ apply() { | ||
match.index === node.tryBlock.pos - 'try'.length && | ||
!this.reported.includes(node) && | ||
!this.reported.has(node.pos) && | ||
isInAsyncFunction(node)) { | ||
@@ -33,27 +33,9 @@ node.tryBlock.statements.forEach(this.visitStatement, this); | ||
if (node.expression !== undefined) | ||
return this.checkReturnExpression(node.expression); | ||
this.checkReturnExpression(node.expression); | ||
return; | ||
} | ||
else if (tsutils_1.isIfStatement(node)) { | ||
if (node.elseStatement !== undefined) | ||
this.visitStatement(node.elseStatement); | ||
return this.visitStatement(node.thenStatement); | ||
} | ||
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); | ||
} | ||
if (node.kind === ts.SyntaxKind.TryStatement) | ||
this.reported.add(node.pos); | ||
for (const statement of utils_1.childStatements(node)) | ||
this.visitStatement(statement); | ||
} | ||
@@ -60,0 +42,0 @@ checkReturnExpression(node) { |
@@ -7,3 +7,6 @@ import * as ts from 'typescript'; | ||
export declare function switchStatements(context: RuleContext): IterableIterator<ts.SwitchStatement>; | ||
export declare function isAsyncFunction(node: ts.Node): node is ts.FunctionLikeDeclaration; | ||
export declare function isAsyncFunction(node: ts.Node): node is ts.FunctionLikeDeclaration & { | ||
body: ts.Block; | ||
}; | ||
export declare function isVariableReassignment(use: VariableUse): boolean; | ||
export declare function childStatements(node: ts.Statement): IterableIterator<ts.Statement>; |
@@ -33,7 +33,10 @@ "use strict"; | ||
case ts.SyntaxKind.ArrowFunction: | ||
if (node.body.kind !== ts.SyntaxKind.Block) | ||
return false; | ||
case ts.SyntaxKind.FunctionExpression: | ||
return tsutils_1.hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword); | ||
break; | ||
default: | ||
return false; | ||
} | ||
return tsutils_1.hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword); | ||
} | ||
@@ -45,2 +48,34 @@ exports.isAsyncFunction = isAsyncFunction; | ||
exports.isVariableReassignment = isVariableReassignment; | ||
function* childStatements(node) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.IfStatement: | ||
yield node.thenStatement; | ||
if (node.elseStatement !== undefined) | ||
yield node.elseStatement; | ||
break; | ||
case ts.SyntaxKind.ForStatement: | ||
case ts.SyntaxKind.ForOfStatement: | ||
case ts.SyntaxKind.ForInStatement: | ||
case ts.SyntaxKind.WhileStatement: | ||
case ts.SyntaxKind.DoStatement: | ||
case ts.SyntaxKind.LabeledStatement: | ||
case ts.SyntaxKind.WithStatement: | ||
yield node.statement; | ||
break; | ||
case ts.SyntaxKind.SwitchStatement: | ||
for (const clause of node.caseBlock.clauses) | ||
yield* clause.statements; | ||
break; | ||
case ts.SyntaxKind.Block: | ||
yield* node.statements; | ||
break; | ||
case ts.SyntaxKind.TryStatement: | ||
yield* node.tryBlock.statements; | ||
if (node.catchClause !== undefined) | ||
yield* node.catchClause.block.statements; | ||
if (node.finallyBlock !== undefined) | ||
yield* (node).finallyBlock.statements; | ||
} | ||
} | ||
exports.childStatements = childStatements; | ||
//# sourceMappingURL=utils.js.map |
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
242450
2836