@fimbul/mimir
Advanced tools
Comparing version 0.23.0-dev.20210108 to 0.23.0-dev.20210110
{ | ||
"name": "@fimbul/mimir", | ||
"version": "0.23.0-dev.20210108", | ||
"version": "0.23.0-dev.20210110", | ||
"description": "Core rules of the Fimbullinter project", | ||
@@ -33,3 +33,3 @@ "main": "recommended.yaml", | ||
"tslib": "^2.0.0", | ||
"tsutils": "^3.19.0" | ||
"tsutils": "^3.19.1" | ||
}, | ||
@@ -36,0 +36,0 @@ "peerDependencies": { |
@@ -21,18 +21,18 @@ "use strict"; | ||
for (const { symbol, name } of utils_1.propertiesOfType(type, names)) | ||
this.checkSymbol(symbol, name, node, node.expression, type); | ||
this.checkSymbol(symbol, name, node, type); | ||
} | ||
checkSymbol(symbol, name, errorNode, lhs, lhsType) { | ||
checkSymbol(symbol, name, node, lhsType) { | ||
const flags = getModifierFlagsOfSymbol(symbol); | ||
if ((lhs === null || lhs === void 0 ? void 0 : lhs.kind) === ts.SyntaxKind.ThisKeyword && | ||
flags & ts.ModifierFlags.Abstract && hasNonMethodDeclaration(symbol)) { | ||
const enclosingClass = getEnclosingClassOfAbstractPropertyAccess(errorNode.parent); | ||
if (node.expression.kind === ts.SyntaxKind.ThisKeyword && | ||
flags & ts.ModifierFlags.Abstract && hasNonPrototypeDeclaration(symbol)) { | ||
const enclosingClass = getEnclosingClassOfAbstractPropertyAccess(node.parent); | ||
if (enclosingClass !== undefined) | ||
return this.addFindingAtNode(errorNode, `Abstract property '${name}' in class '${this.printClass(enclosingClass)}' cannot be accessed during class initialization.`); | ||
return this.addFindingAtNode(node, `Abstract property '${name}' in class '${this.printClass(enclosingClass)}' cannot be accessed during class initialization.`); | ||
} | ||
if ((lhs === null || lhs === void 0 ? void 0 : lhs.kind) === ts.SyntaxKind.SuperKeyword) { | ||
if (hasNonMethodDeclaration(symbol)) | ||
return this.addFindingAtNode(errorNode, "Only public and protected methods of the base class are accessible via the 'super' keyword."); | ||
if (node.expression.kind === ts.SyntaxKind.SuperKeyword && (flags & ts.ModifierFlags.Static) === 0 && !isStaticSuper(node)) { | ||
if (hasNonPrototypeDeclaration(symbol)) | ||
return this.addFindingAtNode(node, "Only public and protected methods and accessors of the base class are accessible via the 'super' keyword."); | ||
if (flags & ts.ModifierFlags.Abstract && | ||
symbol.declarations.every((d) => tsutils_1.hasModifier(d.modifiers, ts.SyntaxKind.AbstractKeyword))) | ||
return this.addFindingAtNode(errorNode, `Abstract method '${name}' in class '${this.printClass(symbol.declarations[0].parent)}' cannot be accessed via the 'super' keyword.`); | ||
return this.addFindingAtNode(node, `Abstract member '${name}' in class '${this.printClass(symbol.declarations[0].parent)}' cannot be accessed via the 'super' keyword.`); | ||
} | ||
@@ -43,16 +43,16 @@ if ((flags & ts.ModifierFlags.NonPublicAccessibilityModifier) === 0) | ||
const declaringClass = symbol.declarations[0].parent; | ||
if (errorNode.pos < declaringClass.pos || errorNode.end > declaringClass.end) | ||
this.failVisibility(errorNode, name, this.printClass(declaringClass), true); | ||
if (node.pos < declaringClass.pos || node.end > declaringClass.end) | ||
this.failVisibility(node, name, this.printClass(declaringClass), true); | ||
} | ||
else { | ||
const declaringClasses = symbol.declarations.map((d) => d.parent); | ||
let enclosingClass = this.findEnclosingClass(errorNode.parent.parent, declaringClasses); | ||
let enclosingClass = this.findEnclosingClass(node.parent, declaringClasses); | ||
if (enclosingClass === undefined) { | ||
if ((flags & ts.ModifierFlags.Static) === 0) | ||
enclosingClass = this.getEnclosingClassFromThisParameter(errorNode.parent.parent, declaringClasses); | ||
enclosingClass = this.getEnclosingClassFromThisParameter(node.parent, declaringClasses); | ||
if (enclosingClass === undefined) | ||
return this.failVisibility(errorNode, name, this.checker.typeToString(lhsType), false); | ||
return this.failVisibility(node, name, this.checker.typeToString(lhsType), false); | ||
} | ||
if ((flags & ts.ModifierFlags.Static) === 0 && !hasBase(lhsType, enclosingClass, isIdentical)) | ||
return this.addFindingAtNode(errorNode, `Property '${name}' is protected and only accessible through an instance of class '${this.checker.typeToString(enclosingClass)}'.`); | ||
return this.addFindingAtNode(node, `Property '${name}' is protected and only accessible through an instance of class '${this.checker.typeToString(enclosingClass)}'.`); | ||
} | ||
@@ -102,5 +102,48 @@ } | ||
exports.Rule = Rule; | ||
function hasNonMethodDeclaration(symbol) { | ||
return !symbol.declarations.every(tsutils_1.isMethodDeclaration); | ||
function isStaticSuper(node) { | ||
while (true) { | ||
switch (node.kind) { | ||
// super in computed property names, heritage clauses and decorators refers to 'this' outside of the current class | ||
case ts.SyntaxKind.ComputedPropertyName: | ||
case ts.SyntaxKind.ExpressionWithTypeArguments: | ||
node = node.parent.parent.parent; | ||
break; | ||
case ts.SyntaxKind.Decorator: | ||
switch (node.parent.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: | ||
case ts.SyntaxKind.ClassExpression: | ||
node = node.parent.parent; | ||
break; | ||
case ts.SyntaxKind.Parameter: | ||
node = node.parent.parent.parent.parent; | ||
break; | ||
default: // class element decorator | ||
node = node.parent.parent.parent; | ||
} | ||
break; | ||
case ts.SyntaxKind.PropertyDeclaration: | ||
case ts.SyntaxKind.MethodDeclaration: | ||
case ts.SyntaxKind.GetAccessor: | ||
case ts.SyntaxKind.SetAccessor: | ||
return tsutils_1.hasModifier(node.modifiers, ts.SyntaxKind.StaticKeyword); | ||
case ts.SyntaxKind.Constructor: | ||
return false; | ||
default: | ||
node = node.parent; | ||
} | ||
} | ||
} | ||
function hasNonPrototypeDeclaration(symbol) { | ||
for (const { kind } of symbol.declarations) { | ||
switch (kind) { | ||
case ts.SyntaxKind.MethodDeclaration: | ||
case ts.SyntaxKind.MethodSignature: | ||
case ts.SyntaxKind.GetAccessor: | ||
case ts.SyntaxKind.SetAccessor: | ||
continue; | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
function getEnclosingClassOfAbstractPropertyAccess(node) { | ||
@@ -124,2 +167,3 @@ while (true) { | ||
return (function recur(t) { | ||
var _a; | ||
if (tsutils_1.isTypeReference(t)) { | ||
@@ -130,4 +174,3 @@ t = t.target; | ||
} | ||
const baseTypes = t.getBaseTypes(); | ||
if (baseTypes !== undefined && baseTypes.some(recur)) | ||
if ((_a = t.getBaseTypes()) === null || _a === void 0 ? void 0 : _a.some(recur)) | ||
return true; | ||
@@ -141,3 +184,3 @@ return tsutils_1.isIntersectionType(t) && t.types.some(recur); | ||
function typeContainsDeclaration(type, declaration) { | ||
return type.symbol !== undefined && type.symbol.declarations !== undefined && type.symbol.declarations.includes(declaration); | ||
return type.symbol.declarations.includes(declaration); | ||
} | ||
@@ -160,5 +203,19 @@ function getThisParameterFromContext(node) { | ||
return; | ||
// this in computed property names, heritage clauses and decorators refers to 'super' outside of the current class | ||
case ts.SyntaxKind.ComputedPropertyName: | ||
case ts.SyntaxKind.ExpressionWithTypeArguments: | ||
node = node.parent.parent.parent; | ||
break; | ||
case ts.SyntaxKind.Decorator: | ||
// skip the declaration the decorator belongs to, because decorators are always applied in the outer context | ||
node = node.parent.parent; | ||
switch (node.parent.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: | ||
case ts.SyntaxKind.ClassExpression: | ||
node = node.parent.parent; | ||
break; | ||
case ts.SyntaxKind.Parameter: | ||
node = node.parent.parent.parent.parent; | ||
break; | ||
default: // class element decorator | ||
node = node.parent.parent.parent; | ||
} | ||
break; | ||
@@ -165,0 +222,0 @@ default: |
Sorry, the diff of this file is not supported yet
423210
5008
Updatedtsutils@^3.19.1