@fimbul/mimir
Advanced tools
Comparing version 0.9.0-dev.20180505 to 0.9.0
@@ -5,3 +5,3 @@ # await-only-promise | ||
Disallows `await`ing a non-Promise value. Also disallows `for-await-of` looping over an Iterable instead of AsyncIterable value. | ||
Disallows `await`ing a non-Promise value. Also enforces `for-await-of` to loop over an `AsyncIterable<any>` or an `Iterable<PromiseLike<any>>` value. | ||
@@ -35,4 +35,5 @@ ## Rationale | ||
for await (const e of iterable) {} // iterating `AsyncIterable` | ||
for await (const e of iterable) {} // iterating `AsyncIterable<any>` | ||
for await (const v of [Promise.resolve(promise)]) {} // iterating `Iterable<PromiseLike<any>>` | ||
} | ||
``` |
@@ -7,2 +7,6 @@ # no-duplicate-spread-property | ||
## Requirements | ||
This rule requires the compiler option `strictNullChecks` to be enabled and TypeScript 2.5 or newer. | ||
## Rationale | ||
@@ -9,0 +13,0 @@ |
{ | ||
"name": "@fimbul/mimir", | ||
"version": "0.9.0-dev.20180505", | ||
"version": "0.9.0", | ||
"description": "Core rules of the Fimbullinter project", | ||
@@ -28,3 +28,3 @@ "main": "recommended.yaml", | ||
"dependencies": { | ||
"@fimbul/ymir": "0.9.0-dev.20180505", | ||
"@fimbul/ymir": "^0.9.0", | ||
"chalk": "^2.3.2", | ||
@@ -31,0 +31,0 @@ "debug": "^3.1.0", |
@@ -33,8 +33,8 @@ # Mímir | ||
[`no-fallthrough`](docs/no-fallthrough.md) | Prevents unintentional fallthough in `switch` statements from one case to another. | Allows more comment variants such as `fallthrough` or `fall through`. | ||
`no-inferred-empty-object` | Warns if a type parameter is inferred as `{}` because the compiler cannot find any inference site. *requires type information* | Really checks every type parameter of function, method and constructor calls. Correctly handles type parameters from JSDoc comments. Recognises type parameter defaults on all merged declarations. | ||
`no-invalid-assertion` | Disallows asserting a literal type to a different literal type of the same widened type, e.g. `'foo' as 'bar'`. *requires type information* | TSLint has no similar rule. | ||
`no-misused-generics` | Detects generic type parameters that cannot be inferred from the functions parameters. It also detects generics that don't enforce any constraint between types. | There's no similar TSLint rule. | ||
`no-nan-compare` | Don't compare with `NaN`, use `isNaN(number)` or `Number.isNaN(number)` instead. | Performance! | ||
`no-return-await` | Warns for unnecesary `return await foo;` when you can simply `return foo;` | The same as TSLint's rule. I wrote both, but this one is faster. | ||
`no-unassigned-variable` | Detects variables that are not initialized and never assigned a value. | There's no similar TSLint rule. | ||
[`no-inferred-empty-object`](docs/no-inferred-empty-object.md) | :mag: Detects type parameters that are inferred as `{}` because the compiler cannot infer a type. | Really checks every type parameter of function, method and constructor calls. Correctly handles type parameters from JSDoc comments. Recognises type parameter defaults on all merged declarations. | ||
[`no-invalid-assertion`](docs/no-invalid-assertion.md) | :mag: Disallows asserting a literal type to a different literal type of the same widened type, e.g. `'foo' as 'bar'`.| TSLint has no similar rule. | ||
[`no-misused-generics`](docs/no-misused-generics.md) | Detects generic type parameters that cannot be inferred from the functions parameters. It also detects generics that don't enforce any constraint between types. | There's no similar TSLint rule. | ||
[`no-nan-compare`](docs/no-nan-compare.md) | Disallows comparing with `NaN`, use `isNaN(number)` or `Number.isNaN(number)` instead. | Performance! | ||
[`no-return-await`](docs/no-return-await.md) | Disallows unnecesary `return await foo;` when you can simply `return foo;` | The same as TSLint's rule. I wrote both, but this one is faster. | ||
[`no-unassigned-variable`](docs/no-unassigned-variable.md) | Detects variables that are not initialized and never assigned a value. | There's no similar TSLint rule. | ||
`no-unreachable-code` | Warns about statements that will never be executed. Works like TypeScript's dead code detection but doesn't fail compilation because it's a lint error. | TSLint removed their `no-unreachable` rule in v4.0.0. | ||
@@ -41,0 +41,0 @@ `no-unsafe-finally` | Forbids control flow statements `return`, `throw`, `break` and `continue` inside the `finally` block of a try statement. | Performance! |
import { TypedRule } from '@fimbul/ymir'; | ||
export declare class Rule extends TypedRule { | ||
apply(): void; | ||
private isPromiseLike(node); | ||
private maybePromiseLike(type, node); | ||
private isThenable(type, node); | ||
private isAsyncIterable(node); | ||
private hasSymbolAsyncIterator(type); | ||
private isIterableOfPromises(type, node); | ||
} |
@@ -14,3 +14,4 @@ "use strict"; | ||
if (tsutils_1.isAwaitExpression(node)) { | ||
if (node.expression.pos === re.lastIndex && !this.isPromiseLike(node.expression)) | ||
if (node.expression.pos === re.lastIndex && | ||
!this.maybePromiseLike(this.checker.getTypeAtLocation(node.expression), 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))); | ||
@@ -27,4 +28,4 @@ } | ||
} | ||
isPromiseLike(node) { | ||
const type = this.checker.getApparentType(this.checker.getTypeAtLocation(node)); | ||
maybePromiseLike(type, node) { | ||
type = this.checker.getApparentType(type); | ||
if (type.flags & ts.TypeFlags.Any) | ||
@@ -46,3 +47,3 @@ return true; | ||
for (const t of tsutils_1.unionTypeParts(type)) | ||
if (this.hasSymbolAsyncIterator(t)) | ||
if (this.hasSymbolAsyncIterator(t) || this.isIterableOfPromises(t, node)) | ||
return true; | ||
@@ -52,4 +53,32 @@ return false; | ||
hasSymbolAsyncIterator(type) { | ||
return type.getProperties().some((prop) => prop.name === '__@asyncIterator'); | ||
return type.getProperties().some((prop) => symbolName(prop) === '__@asyncIterator'); | ||
} | ||
isIterableOfPromises(type, node) { | ||
const symbol = type.getProperties().find((prop) => symbolName(prop) === '__@iterator'); | ||
if (symbol === undefined) | ||
return false; | ||
const t = this.checker.getApparentType(this.checker.getTypeOfSymbolAtLocation(symbol, node)); | ||
if (t.flags & ts.TypeFlags.Any) | ||
return true; | ||
for (const signature of t.getCallSignatures()) { | ||
const returnType = this.checker.getApparentType(signature.getReturnType()); | ||
if (returnType.flags & ts.TypeFlags.Any) | ||
return true; | ||
const next = returnType.getProperty('next'); | ||
if (next === undefined) | ||
continue; | ||
const nextType = this.checker.getApparentType(this.checker.getTypeOfSymbolAtLocation(next, node)); | ||
if (nextType.flags & ts.TypeFlags.Any) | ||
return true; | ||
for (const nextSignature of nextType.getCallSignatures()) { | ||
const nextReturnType = this.checker.getApparentType(nextSignature.getReturnType()); | ||
if (nextReturnType.flags & ts.TypeFlags.Any) | ||
return true; | ||
const value = nextReturnType.getProperty('value'); | ||
if (value !== undefined && this.maybePromiseLike(this.checker.getTypeOfSymbolAtLocation(value, node), node)) | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
}; | ||
@@ -60,2 +89,5 @@ Rule = tslib_1.__decorate([ | ||
exports.Rule = Rule; | ||
function symbolName(s) { | ||
return s.escapedName === undefined ? s.name : s.escapedName; | ||
} | ||
//# sourceMappingURL=await-only-promise.js.map |
import { AbstractRule } from '@fimbul/ymir'; | ||
export declare class Rule extends AbstractRule { | ||
apply(): void; | ||
private getLiteralValue(node, cb); | ||
private getLiteralValue(node); | ||
} |
@@ -22,11 +22,19 @@ "use strict"; | ||
expressionsSeen.add(text); | ||
this.getLiteralValue(clause.expression, (v) => { | ||
if (valuesSeen.has(v)) | ||
return this.addFailureAtNode(clause.expression, `Duplicate 'case ${typeof v === 'string' ? `"${v}"` : String(v)}'.`); | ||
valuesSeen.add(v); | ||
}); | ||
const literals = this.getLiteralValue(clause.expression); | ||
switch (literals.length) { | ||
case 0: | ||
break; | ||
case 1: | ||
if (valuesSeen.has(literals[0])) | ||
this.addFailureAtNode(clause.expression, `Duplicate 'case ${formatPrimitive(literals[0])}'.`); | ||
valuesSeen.add(literals[0]); | ||
break; | ||
default: | ||
if (literals.every((v) => valuesSeen.has(v))) | ||
this.addFailureAtNode(clause.expression, `Duplicate 'case ${literals.map(formatPrimitive).join(' | ')}'.`); | ||
} | ||
} | ||
} | ||
} | ||
getLiteralValue(node, cb) { | ||
getLiteralValue(node) { | ||
let prefixFn = identity; | ||
@@ -36,3 +44,3 @@ while (tsutils_1.isPrefixUnaryExpression(node)) { | ||
if (next === undefined) | ||
return; | ||
return []; | ||
prefixFn = next; | ||
@@ -42,26 +50,37 @@ node = node.operand; | ||
if (tsutils_1.isTextualLiteral(node)) | ||
return cb(prefixFn(node.text)); | ||
return [prefixFn(node.text)]; | ||
if (tsutils_1.isNumericLiteral(node)) | ||
return cb(prefixFn(+node.text)); | ||
return [prefixFn(+node.text)]; | ||
if (node.kind === ts.SyntaxKind.NullKeyword) | ||
return cb(prefixFn(null)); | ||
return [prefixFn(null)]; | ||
if (tsutils_1.isIdentifier(node) && node.originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) | ||
return cb(prefixFn(undefined)); | ||
return [prefixFn(undefined)]; | ||
if (node.kind === ts.SyntaxKind.TrueKeyword) | ||
return cb(prefixFn(true)); | ||
return [prefixFn(true)]; | ||
if (node.kind === ts.SyntaxKind.FalseKeyword) | ||
return cb(prefixFn(false)); | ||
return [prefixFn(false)]; | ||
if (this.program === undefined) | ||
return; | ||
return []; | ||
const checker = this.program.getTypeChecker(); | ||
let type = checker.getTypeAtLocation(node); | ||
type = checker.getBaseConstraintOfType(type) || type; | ||
if (tsutils_1.isLiteralType(type)) | ||
return cb(prefixFn(type.value)); | ||
if (type.flags & ts.TypeFlags.BooleanLiteral) | ||
return cb(prefixFn(type.intrinsicName === 'true')); | ||
if (type.flags & ts.TypeFlags.Undefined) | ||
return cb(prefixFn(undefined)); | ||
if (type.flags & ts.TypeFlags.Null) | ||
return cb(prefixFn(null)); | ||
const result = new Set(); | ||
for (const t of tsutils_1.unionTypeParts(type)) { | ||
if (tsutils_1.isLiteralType(t)) { | ||
result.add(prefixFn(t.value)); | ||
} | ||
else if (t.flags & ts.TypeFlags.BooleanLiteral) { | ||
result.add(prefixFn(t.intrinsicName === 'true')); | ||
} | ||
else if (t.flags & ts.TypeFlags.Undefined) { | ||
result.add(prefixFn(undefined)); | ||
} | ||
else if (t.flags & ts.TypeFlags.Null) { | ||
result.add(prefixFn(null)); | ||
} | ||
else { | ||
return []; | ||
} | ||
} | ||
return Array.from(result); | ||
} | ||
@@ -90,2 +109,5 @@ }; | ||
} | ||
function formatPrimitive(v) { | ||
return typeof v === 'string' ? `"${v}"` : String(v); | ||
} | ||
//# sourceMappingURL=no-duplicate-case.js.map |
@@ -31,5 +31,12 @@ "use strict"; | ||
for (let i = properties.length - 1; i >= 0; --i) { | ||
const info = this.getPropertyInfo(properties[i]); | ||
if (info.known && info.names.every((name) => propertiesSeen.has(name))) | ||
this.addFailureAtNode(properties[i], 'Property is overridden later.'); | ||
const property = properties[i]; | ||
const info = this.getPropertyInfo(property); | ||
if (info.known && info.names.every((name) => propertiesSeen.has(name))) { | ||
if (property.kind === ts.SyntaxKind.SpreadAssignment) { | ||
this.addFailureAtNode(property, 'All properties of this object are overridden later.'); | ||
} | ||
else { | ||
this.addFailureAtNode(property.name, `Property '${property.name.getText(this.sourceFile)}' is overridden later.`); | ||
} | ||
} | ||
for (const name of info.assignedNames) | ||
@@ -36,0 +43,0 @@ propertiesSeen.add(name); |
@@ -125,2 +125,4 @@ "use strict"; | ||
function getTypeParameters(node) { | ||
if (node.typeParameters !== undefined) | ||
return node.typeParameters; | ||
if (node.flags & ts.NodeFlags.JavaScriptFile) { | ||
@@ -130,4 +132,4 @@ const tag = ts.getJSDocTemplateTag(node); | ||
} | ||
return node.typeParameters; | ||
return; | ||
} | ||
//# sourceMappingURL=no-inferred-empty-object.js.map |
@@ -141,3 +141,7 @@ "use strict"; | ||
return false; | ||
return findupFunction(node.parent.parent.parent) === findupFunction(declaration.parent.parent.parent); | ||
const declaringFunctionScope = findupFunction(declaration.parent.parent.parent); | ||
let useFunctionScope = findupFunction(node.parent.parent); | ||
while (useFunctionScope !== declaringFunctionScope && isIife(useFunctionScope)) | ||
useFunctionScope = findupFunction(useFunctionScope.parent.parent); | ||
return useFunctionScope === declaringFunctionScope; | ||
} | ||
@@ -149,2 +153,14 @@ function findupFunction(node) { | ||
} | ||
function isIife(node) { | ||
if (!tsutils_1.isFunctionExpression(node) && !tsutils_1.isArrowFunction(node) || | ||
node.asteriskToken !== undefined || | ||
tsutils_1.hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword)) | ||
return false; | ||
let prev; | ||
do { | ||
prev = node; | ||
node = node.parent; | ||
} while (node.kind === ts.SyntaxKind.ParenthesizedExpression); | ||
return tsutils_1.isCallExpression(node) && node.expression === prev; | ||
} | ||
function couldBeTupleType(type) { | ||
@@ -151,0 +167,0 @@ const properties = type.getProperties(); |
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
316170
148
3499
+ Added@fimbul/ymir@0.9.0(transitive)
- Removed@fimbul/ymir@0.9.0-dev.20180505(transitive)
Updated@fimbul/ymir@^0.9.0