UNPKG

ui5plugin-linter

Version:
227 lines (226 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WrongFieldMethodLinter = void 0; const FieldsAndMethodForPositionBeforeCurrentStrategy_1 = require("ui5plugin-parser/dist/classes/parsing/jsparser/typesearch/FieldsAndMethodForPositionBeforeCurrentStrategy"); const CustomJSClass_1 = require("ui5plugin-parser/dist/classes/parsing/ui5class/js/CustomJSClass"); const RangeAdapter_1 = require("ui5plugin-parser/dist/classes/parsing/util/range/adapters/RangeAdapter"); const Linter_1 = require("../../Linter"); const JSLinter_1 = require("./abstraction/JSLinter"); class WrongFieldMethodLinter extends JSLinter_1.JSLinter { constructor() { super(...arguments); this.className = Linter_1.JSLinters.WrongFieldMethodLinter; } _getErrors(document) { return this._getLintingErrors(document); } _getLintingErrors(document) { let errors = []; const currentClassName = this._parser.fileReader.getClassNameFromPath(document.fileName); if (currentClassName) { const UIClass = this._parser.classFactory.getUIClass(currentClassName); if (UIClass instanceof CustomJSClass_1.CustomJSClass) { const acornMethods = UIClass.acornMethodsAndFields .filter(fieldOrMethod => fieldOrMethod.value.type === "FunctionExpression") .map((node) => node.value.body); acornMethods.forEach((method) => { if (method.body) { method.body.forEach((node) => { const validationErrors = this._getErrorsForExpression(node, UIClass, document); errors = errors.concat(validationErrors); }); } }); } } return errors; } _getErrorsForExpression(node, UIClass, document, errors = [], droppedNodes = [], errorNodes = []) { if (droppedNodes.includes(node)) { return []; } const currentClassName = UIClass.className; if (node.type === "MemberExpression") { const strategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this._parser.syntaxAnalyser, this._parser); const nodeStack = strategy.getStackOfNodesForPosition(currentClassName, node.end); if (nodeStack.length > 0) { const nodes = []; while (nodeStack.length > 0) { let nextNode = nodeStack.shift(); nodes.push(nextNode); nextNode = nodeStack[0]; if (nextNode?.type === "CallExpression") { nextNode = nodeStack.shift(); nodes.push(nextNode); } const className = this._parser.syntaxAnalyser.findClassNameForStack(nodes.concat([]), currentClassName, currentClassName, true); const isException = this._checkIfClassNameIsException(className); if (!className || isException || (nextNode?.type === "Identifier" && nextNode?.name === "sap")) { droppedNodes.push(...nodeStack); break; } const classNames = className.split("|"); nextNode = nodeStack[0]; if (!nextNode) { nextNode = node; } const nextNodeName = nextNode.property?.name; const nodeText = UIClass.classText.substring(nextNode.start, nextNode.end); if (!nodeText.endsWith("]") && !errorNodes.includes(nextNode)) { const isMethodException = this._configHandler.checkIfMemberIsException(className, nextNodeName); if (nextNodeName && !isMethodException) { const singleFieldsAndMethods = this._getFieldsAndMethods(classNames, strategy, nextNode, nextNodeName); if (!singleFieldsAndMethods) { const isAnyMethodAnException = classNames.length > 0 ? classNames.some(className => this._configHandler.checkIfMemberIsException(className, nextNodeName)) : false; if (!isAnyMethodAnException) { const shouldBreak = this._fillNonExistantMethodError(className, nextNodeName, nextNode, errorNodes, errors, UIClass, document); if (shouldBreak) { break; } } } else { const shouldBreak = this._fillAccessLevelModifierErrors(singleFieldsAndMethods, nextNodeName, document, nextNode, errorNodes, errors, UIClass, className); if (shouldBreak) { break; } else { this._fillDeprecationErrors(singleFieldsAndMethods, nextNodeName, nextNode, errorNodes, errors, UIClass, className, document); } } } } else if (nodeText.endsWith("]")) { droppedNodes.push(nextNode); if (nextNode.property) { droppedNodes.push(nextNode.property); } break; } } } } const innerNodes = this._parser.syntaxAnalyser.getContent(node); if (innerNodes) { innerNodes.forEach((node) => this._getErrorsForExpression(node, UIClass, document, errors, droppedNodes, errorNodes)); } return errors; } _fillDeprecationErrors(singleFieldsAndMethods, nextNodeName, nextNode, errorNodes, errors, UIClass, className, document) { const isMethodException = this._configHandler.checkIfMemberIsException(className, nextNodeName); if (!isMethodException) { const allMembers = [...singleFieldsAndMethods.fields, ...singleFieldsAndMethods.methods]; const member = allMembers.find(member => member.name === nextNodeName); if (member?.deprecated) { const range = RangeAdapter_1.RangeAdapter.acornLocationToRange(nextNode.property.loc); errorNodes.push(nextNode); errors.push({ message: `"${nextNodeName}" is deprecated`, code: "UI5Plugin", source: this.className, range: range, acornNode: nextNode, className: UIClass.className, tags: [Linter_1.DiagnosticTag.Deprecated], methodName: nextNodeName, sourceClassName: className, severity: this._configHandler.getSeverity(this.className), fsPath: document.fileName }); } } } _fillNonExistantMethodError(className, nextNodeName, nextNode, errorNodes, errors, UIClass, document) { let shouldBreak = false; if (className.includes("__map__")) { className = "map"; } const isMethodException = this._configHandler.checkIfMemberIsException(className, nextNodeName); if (!isMethodException) { const range = RangeAdapter_1.RangeAdapter.acornLocationToRange(nextNode.property.loc); errorNodes.push(nextNode); errors.push({ message: `"${nextNodeName}" does not exist in "${className}"`, code: "UI5Plugin", source: this.className, range: range, acornNode: nextNode, className: UIClass.className, type: Linter_1.CustomDiagnosticType.NonExistentMethod, methodName: nextNodeName, sourceClassName: className, severity: this._configHandler.getSeverity(this.className), fsPath: document.fileName }); shouldBreak = true; } return shouldBreak; } _fillAccessLevelModifierErrors(singleFieldsAndMethods, nextNodeName, document, nextNode, errorNodes, errors, UIClass, className) { let shouldBreak = false; const member = singleFieldsAndMethods.fields.find(field => field.name === nextNodeName) || singleFieldsAndMethods.methods.find(method => method.name === nextNodeName); const isIgnored = !!member?.ui5ignored; if (!isIgnored) { let sErrorMessage = ""; if (member?.visibility === "protected") { const currentDocumentClassName = this._parser.fileReader.getClassNameFromPath(document.fileName); if (currentDocumentClassName && !this._parser.classFactory.isClassAChildOfClassB(currentDocumentClassName, singleFieldsAndMethods.className)) { sErrorMessage = `"${nextNodeName}" is a protected member of class "${member.owner}"`; } } else if (member?.visibility === "private") { const currentDocumentClassName = this._parser.fileReader.getClassNameFromPath(document.fileName); if (currentDocumentClassName && member.owner !== currentDocumentClassName) { sErrorMessage = `"${nextNodeName}" is a private member of class "${member.owner}"`; } } if (sErrorMessage) { const range = RangeAdapter_1.RangeAdapter.acornLocationToRange(nextNode.property.loc); errorNodes.push(nextNode); errors.push({ message: sErrorMessage, code: "UI5Plugin", source: this.className, range: range, acornNode: nextNode, methodName: nextNodeName, className: UIClass.className, sourceClassName: className, severity: this._configHandler.getSeverity(this.className), fsPath: document.fileName }); shouldBreak = true; } } return shouldBreak; } _getFieldsAndMethods(classNames, strategy, nextNode, nextNodeName) { const fieldsAndMethods = classNames.map(className => strategy.destructureFieldsAndMethodsAccordingToMapParams(className)); const singleFieldsAndMethods = fieldsAndMethods.find(fieldsAndMethods => { if (nextNode && fieldsAndMethods && nextNodeName) { const method = fieldsAndMethods.methods.find(method => method.name === nextNodeName); const field = fieldsAndMethods.fields.find(field => field.name === nextNodeName); return method || field; } return false; }); return singleFieldsAndMethods; } _checkIfClassNameIsException(className = "") { let isException = false; const exceptions = ["void", "any", "array"]; if (className.split(".").length === 1) { isException = true; } else if (exceptions.includes(className)) { isException = true; } return isException; } } exports.WrongFieldMethodLinter = WrongFieldMethodLinter; WrongFieldMethodLinter.timePerChar = 0;