UNPKG

@abaplint/core

Version:
265 lines • 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KeywordCase = exports.KeywordCaseConf = exports.KeywordCaseStyle = void 0; const issue_1 = require("../issue"); const _abap_rule_1 = require("./_abap_rule"); const nodes_1 = require("../abap/nodes"); const _statement_1 = require("../abap/2_statements/statements/_statement"); const tokens_1 = require("../abap/1_lexer/tokens"); const objects_1 = require("../objects"); const _basic_rule_config_1 = require("./_basic_rule_config"); const Statements = require("../abap/2_statements/statements"); const Expressions = require("../abap/2_statements/expressions"); const _irule_1 = require("./_irule"); const ddic_1 = require("../ddic"); const virtual_position_1 = require("../virtual_position"); const edit_helper_1 = require("../edit_helper"); var KeywordCaseStyle; (function (KeywordCaseStyle) { KeywordCaseStyle["Upper"] = "upper"; KeywordCaseStyle["Lower"] = "lower"; })(KeywordCaseStyle || (exports.KeywordCaseStyle = KeywordCaseStyle = {})); class KeywordCaseConf extends _basic_rule_config_1.BasicRuleConfig { constructor() { super(...arguments); this.style = KeywordCaseStyle.Upper; /** Ignore global exception classes */ this.ignoreExceptions = true; this.ignoreLowerClassImplmentationStatement = true; this.ignoreGlobalClassDefinition = false; this.ignoreGlobalInterface = false; this.ignoreFunctionModuleName = false; // this ignores keywords in CLASS/ENDCLASS statements of a global class (and only in them, the rest is checked) this.ignoreGlobalClassBoundaries = false; /** A list of keywords to be ignored */ this.ignoreKeywords = []; } } exports.KeywordCaseConf = KeywordCaseConf; class Skip { constructor(conf) { this.skip = false; this.isGlobalClass = false; this.isGlobalIf = false; this.conf = conf; } skipStatement(statement) { const get = statement.get(); if (get instanceof _statement_1.Unknown || get instanceof _statement_1.NativeSQL || get instanceof _statement_1.MacroContent || get instanceof _statement_1.MacroCall || statement.getFirstToken().getStart() instanceof virtual_position_1.VirtualPosition || get instanceof _statement_1.Comment) { return true; } if (this.conf.ignoreGlobalClassBoundaries) { const node = get; if (node instanceof Statements.Interface && statement.findFirstExpression(Expressions.ClassGlobal)) { this.isGlobalIf = true; return true; } else if (this.isGlobalIf === true && node instanceof Statements.EndInterface) { return true; } if (node instanceof Statements.ClassDefinition && statement.findFirstExpression(Expressions.ClassGlobal)) { this.isGlobalClass = true; return true; } else if (this.isGlobalClass === true && (node instanceof Statements.EndClass || node instanceof Statements.ClassImplementation)) { return true; } } if (this.conf.ignoreGlobalClassDefinition) { if (get instanceof Statements.ClassDefinition && statement.findFirstExpression(Expressions.ClassGlobal)) { this.skip = true; return true; } else if (this.skip === true && get instanceof Statements.EndClass) { this.skip = false; return true; } else if (this.skip === true) { return true; } } if (this.conf.ignoreGlobalInterface) { if (get instanceof Statements.Interface && statement.findFirstExpression(Expressions.ClassGlobal)) { this.skip = true; return true; } else if (this.skip === true && get instanceof Statements.EndInterface) { this.skip = false; return true; } else if (this.skip === true) { return true; } } return false; } } class KeywordCase extends _abap_rule_1.ABAPRule { constructor() { super(...arguments); this.conf = new KeywordCaseConf(); } getMetadata() { return { key: "keyword_case", title: "Keyword case", shortDescription: `Checks that keywords have the same case. Non-keywords must be lower case.`, extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#use-your-pretty-printer-team-settings`, tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix], badExample: `write 'hello world'.`, goodExample: `WRITE 'hello world'.`, }; } getConfig() { return this.conf; } setConfig(conf) { this.conf = conf; if (this.conf === undefined) { this.conf = new KeywordCaseConf(); } if (this.conf.style === undefined) { this.conf = new KeywordCaseConf(); } if (this.conf.ignoreExceptions === undefined) { this.conf.ignoreExceptions = new KeywordCaseConf().ignoreExceptions; } } runParsed(file, obj) { const issues = []; const ddic = new ddic_1.DDIC(this.reg); const MAX_ISSUES = 100; if (this.conf.ignoreExceptions && obj instanceof objects_1.Class) { const definition = obj.getClassDefinition(); if (definition === undefined || ddic.isException(definition, obj)) { return []; } } const skip = new Skip(this.getConfig()); let prev = undefined; for (const statement of file.getStatements()) { if (skip.skipStatement(statement) === true) { continue; } let result = this.traverse(statement, statement.get()); if (result.length > 0) { if (prev && result[0].token.getStart().equals(prev.getStart())) { continue; } if (statement.getColon() !== undefined) { // if its a chained statement, go token by token result = [result[0]]; } const issue = this.build(result, file); issues.push(issue); if (issues.length > MAX_ISSUES) { break; } prev = result[0].token; } } return issues; } ////////////////// build(tokens, file) { const first = tokens[0]; const firstToken = tokens[0].token; const lastToken = tokens[tokens.length - 1].token; const firstTokenValue = firstToken.getStr(); let description = ""; if (first.keyword === true) { description = `Keyword should be ${this.conf.style} case: "${firstTokenValue}"`; } else { description = `Identifiers should be lower case: "${firstTokenValue}"`; } const draft = new edit_helper_1.EditDraft(file); for (const token of tokens) { const str = token.token.getStr(); const pos = token.token.getStart(); if (token.keyword === true) { if (this.conf.style === KeywordCaseStyle.Lower) { draft.replace(pos, str.toLowerCase()); } else { draft.replace(pos, str.toUpperCase()); } } else { draft.replace(pos, str.toLowerCase()); } } const fix = draft.toEdit(); return issue_1.Issue.atRange(file, firstToken.getStart(), lastToken.getEnd(), description, this.getMetadata().key, this.conf.severity, fix); } /** returns a list of tokens which violates the keyword_case rule */ traverse(s, parent) { let ret = []; const children = s.getChildren(); for (let i = 0; i < children.length; i++) { const child = children[i]; if (child instanceof nodes_1.TokenNodeRegex) { const next = children[i + 1]; if (this.conf.ignoreLowerClassImplmentationStatement && parent instanceof Statements.ClassImplementation) { continue; } const str = child.get().getStr(); const upper = str.toUpperCase(); // todo, this is a hack, the parser should recongize OTHERS/TEXT as a keyword if (upper === "OTHERS" || (upper === "TEXT" && (next === null || next === void 0 ? void 0 : next.concatTokens()) === "-")) { continue; } if (this.conf.ignoreFunctionModuleName === true && parent instanceof Statements.FunctionModule && upper !== "FUNCTION") { continue; } // todo, this is a hack, the parser should recigize SCREEN as a keyword if (upper === "SCREEN" && (parent instanceof Statements.ModifyDatabase || parent instanceof Statements.ModifyInternal || parent instanceof Statements.Loop)) { continue; } if (str !== str.toLowerCase() && child.get() instanceof tokens_1.Identifier) { ret.push({ token: child.get(), keyword: false }); } } else if (child instanceof nodes_1.TokenNode) { const str = child.get().getStr(); if (this.violatesRule(str) && child.get() instanceof tokens_1.Identifier) { ret.push({ token: child.get(), keyword: true }); } } else if (child instanceof nodes_1.ExpressionNode) { ret = ret.concat(this.traverse(child, parent)); } else { throw new Error("keyword_case, traverseStatement, unexpected node type"); } } return ret; } violatesRule(keyword) { if (this.conf.ignoreKeywords && this.conf.ignoreKeywords.map(k => { return k.toUpperCase(); }).includes(keyword.toUpperCase())) { return false; } if (this.conf.style === KeywordCaseStyle.Lower) { return keyword !== keyword.toLowerCase(); } else if (this.conf.style === KeywordCaseStyle.Upper) { return keyword !== keyword.toUpperCase(); } return false; } } exports.KeywordCase = KeywordCase; //# sourceMappingURL=keyword_case.js.map