@fimbul/mimir
Advanced tools
Comparing version 0.23.0-dev.20210114 to 0.23.0-dev.20210116
{ | ||
"name": "@fimbul/mimir", | ||
"version": "0.23.0-dev.20210114", | ||
"version": "0.23.0-dev.20210116", | ||
"description": "Core rules of the Fimbullinter project", | ||
@@ -5,0 +5,0 @@ "main": "recommended.yaml", |
@@ -6,9 +6,17 @@ "use strict"; | ||
const tsutils_1 = require("tsutils"); | ||
function getRestrictedElementAccessError(checker, symbol, name, node, lhsType) { | ||
function getRestrictedElementAccessError(checker, symbol, name, node, lhsType, compilerOptions) { | ||
const flags = getModifierFlagsOfSymbol(symbol); | ||
if (node.expression.kind === ts.SyntaxKind.ThisKeyword && | ||
flags & ts.ModifierFlags.Abstract && hasNonPrototypeDeclaration(symbol)) { | ||
const enclosingClass = getEnclosingClassOfAbstractPropertyAccess(node.parent); | ||
if (enclosingClass !== undefined) | ||
return `Abstract property '${name}' in class '${printClass(enclosingClass, checker)}' cannot be accessed during class initialization.`; | ||
if (node.expression.kind === ts.SyntaxKind.ThisKeyword && hasNonPrototypeDeclaration(symbol)) { | ||
const useDuringClassInitialization = getPropertyDeclarationOrConstructorAccessingProperty(node.parent); | ||
if (useDuringClassInitialization !== undefined) { | ||
if (flags & ts.ModifierFlags.Abstract) | ||
return `Abstract property '${name}' in class '${printClass(tsutils_1.getSymbolOfClassLikeDeclaration(useDuringClassInitialization.parent, checker), checker)}' cannot be accessed during class initialization.`; | ||
if ( | ||
// checking use before assign in constructor requires control flow graph | ||
useDuringClassInitialization.kind !== ts.SyntaxKind.Constructor && | ||
// only checking read access | ||
tsutils_1.getAccessKind(node) & tsutils_1.AccessKind.Read && | ||
isPropertyUsedBeforeAssign(symbol.valueDeclaration, useDuringClassInitialization, compilerOptions, checker)) | ||
return `Property '${name}' is used before its initialization.`; | ||
} | ||
} | ||
@@ -20,3 +28,3 @@ if (node.expression.kind === ts.SyntaxKind.SuperKeyword && (flags & ts.ModifierFlags.Static) === 0 && !isStaticSuper(node)) { | ||
symbol.declarations.every((d) => tsutils_1.hasModifier(d.modifiers, ts.SyntaxKind.AbstractKeyword))) | ||
return `Abstract member '${name}' in class '${printClass(symbol.declarations[0].parent, checker)}' cannot be accessed via the 'super' keyword.`; | ||
return `Abstract member '${name}' in class '${printClass(getDeclaringClassOfMember(symbol.valueDeclaration, checker), checker)}' cannot be accessed via the 'super' keyword.`; | ||
} | ||
@@ -26,8 +34,8 @@ if ((flags & ts.ModifierFlags.NonPublicAccessibilityModifier) === 0) | ||
if (flags & ts.ModifierFlags.Private) { | ||
const declaringClass = symbol.declarations[0].parent; | ||
if (node.pos < declaringClass.pos || node.end > declaringClass.end) | ||
const declaringClass = getDeclaringClassOfMember(symbol.valueDeclaration, checker); | ||
if (node.pos < declaringClass.valueDeclaration.pos || node.end > declaringClass.valueDeclaration.end) | ||
return failVisibility(name, printClass(declaringClass, checker), true); | ||
} | ||
else { | ||
const declaringClasses = symbol.declarations.map((d) => d.parent); | ||
const declaringClasses = symbol.declarations.map((d) => getDeclaringClassOfMember(d, checker).valueDeclaration); | ||
let enclosingClass = findEnclosingClass(node.parent, declaringClasses, checker); | ||
@@ -65,4 +73,4 @@ if (enclosingClass === undefined) { | ||
} | ||
function printClass(declaration, checker) { | ||
return checker.typeToString(tsutils_1.getInstanceTypeOfClassLikeDeclaration(declaration, checker)); | ||
function printClass(symbol, checker) { | ||
return checker.typeToString(checker.getDeclaredTypeOfSymbol(symbol)); | ||
} | ||
@@ -130,15 +138,8 @@ function getEnclosingClassFromThisParameter(node, baseClasses, checker) { | ||
} | ||
function getEnclosingClassOfAbstractPropertyAccess(node) { | ||
function getPropertyDeclarationOrConstructorAccessingProperty(node) { | ||
while (true) { | ||
if (tsutils_1.isFunctionScopeBoundary(node)) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.ClassDeclaration: | ||
case ts.SyntaxKind.ClassExpression: | ||
return node; | ||
case ts.SyntaxKind.Constructor: | ||
return node.parent; | ||
default: | ||
return; | ||
} | ||
} | ||
if (tsutils_1.isFunctionScopeBoundary(node)) | ||
return node.kind === ts.SyntaxKind.Constructor ? node : undefined; | ||
if (node.kind === ts.SyntaxKind.PropertyDeclaration) | ||
return node; | ||
node = node.parent; | ||
@@ -210,2 +211,58 @@ } | ||
} | ||
function isPropertyUsedBeforeAssign(prop, usedIn, compilerOptions, checker) { | ||
if (tsutils_1.isParameterDeclaration(prop)) | ||
// when emitting native class fields, parameter properties cannot be used in other property's initializers in the same class | ||
return prop.parent.parent === usedIn.parent && isEmittingNativeClassFields(compilerOptions); | ||
// this also matches assignment declarations in the same class; we could handle them, but TypeScript doesn't, so why bother | ||
if (prop.parent !== usedIn.parent) | ||
return false; | ||
// property is declared before use in the same class | ||
if (prop.pos < usedIn.pos) { | ||
// OK if immediately initialized | ||
if (prop.initializer !== undefined || prop.exclamationToken !== undefined) | ||
return false; | ||
// NOT OK if [[Define]] semantics are used, because it overrides the property from the base class | ||
if (!tsutils_1.hasModifier(prop.modifiers, ts.SyntaxKind.DeclareKeyword) && | ||
tsutils_1.isCompilerOptionEnabled(compilerOptions, 'useDefineForClassFields')) | ||
return true; | ||
} | ||
return tsutils_1.getBaseClassMemberOfClassElement(prop, checker) === undefined; | ||
} | ||
function isEmittingNativeClassFields(compilerOptions) { | ||
return compilerOptions.target === ts.ScriptTarget.ESNext && tsutils_1.isCompilerOptionEnabled(compilerOptions, 'useDefineForClassFields'); | ||
} | ||
function getDeclaringClassOfMember(node, checker) { | ||
switch (node.kind) { | ||
case ts.SyntaxKind.PropertyDeclaration: // regular property | ||
return tsutils_1.getSymbolOfClassLikeDeclaration(node.parent, checker); | ||
case ts.SyntaxKind.Parameter: // parameter property | ||
return tsutils_1.getSymbolOfClassLikeDeclaration(node.parent.parent, checker); | ||
case ts.SyntaxKind.MethodDeclaration: | ||
case ts.SyntaxKind.GetAccessor: | ||
case ts.SyntaxKind.SetAccessor: | ||
if (tsutils_1.isClassLikeDeclaration(node.parent)) | ||
return tsutils_1.getSymbolOfClassLikeDeclaration(node.parent, checker); | ||
// falls through | ||
// JS special property assignment declarations | ||
case ts.SyntaxKind.PropertyAssignment: // 'C.prototype = {method() {}, prop: 1, shorthand, get a() {return 1;}, set a(v) {}} | ||
case ts.SyntaxKind.ShorthandPropertyAssignment: | ||
return checker.getSymbolAtLocation(node.parent.parent.left.expression); | ||
case ts.SyntaxKind.BinaryExpression: // this.foo = 1; | ||
node = node.left; | ||
// falls through | ||
case ts.SyntaxKind.PropertyAccessExpression: // 'this.foo', 'C.foo' or 'C.prototype.foo' | ||
case ts.SyntaxKind.ElementAccessExpression: | ||
node = node.expression; | ||
switch (node.kind) { | ||
case ts.SyntaxKind.PropertyAccessExpression: | ||
case ts.SyntaxKind.ElementAccessExpression: | ||
node = node.expression; | ||
} | ||
return checker.getSymbolAtLocation(node); | ||
/* istanbul ignore next */ | ||
default: | ||
throw new Error(`unhandled property declaration kind ${node.kind}`); | ||
// this doesn't handle access modifier: 'Object.defineProperty(C, 'foo', ...)' or 'Object.defineProperty(C.prototype, 'foo', ...)' | ||
} | ||
} | ||
//# sourceMappingURL=restricted-property.js.map |
@@ -25,3 +25,3 @@ "use strict"; | ||
for (const { symbol, name } of utils_1.propertiesOfType(type, names)) { | ||
const error = restricted_property_1.getRestrictedElementAccessError(this.checker, symbol, name, node, type); | ||
const error = restricted_property_1.getRestrictedElementAccessError(this.checker, symbol, name, node, type, this.context.compilerOptions); | ||
if (error !== undefined) | ||
@@ -28,0 +28,0 @@ this.addFindingAtNode(node, error); |
@@ -38,3 +38,3 @@ "use strict"; | ||
} | ||
return restricted_property_1.getRestrictedElementAccessError(this.checker, symbol, name, node, type) !== undefined; | ||
return restricted_property_1.getRestrictedElementAccessError(this.checker, symbol, name, node, type, this.context.compilerOptions) !== undefined; | ||
} | ||
@@ -41,0 +41,0 @@ }; |
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
438647
5152