All files / src/rules ambiguous_statement.ts

98% Statements 98/100
95.45% Branches 21/22
100% Functions 8/8
98% Lines 98/100

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1001x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11021x 11021x 11021x 11021x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 32932x 11021x 11021x 9x 9x 11021x 11021x 10502x 10502x 11021x 11021x 244x 244x 11021x 11021x 275x 275x 275x     275x 275x 1496x 1496x 1496x 1x 1496x 5x 1495x 3x 1490x 2x 1487x 2x 1485x 1x 1x 1496x 1496x 9x 9x 9x 1496x 1496x 275x 275x 275x 11021x 11021x 6x 6x 6x 6x 11021x 11021x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11021x 11021x
import {Issue} from "../issue";
import * as Statements from "../abap/2_statements/statements";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {StatementNode} from "../abap/nodes";
import {IStatement} from "../abap/2_statements/statements/_statement";
import {Combi} from "../abap/2_statements/combi";
import {IRegistry} from "../_iregistry";
import {Release, releaseAtLeast} from "../version";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
 
export class AmbiguousStatementConf extends BasicRuleConfig {
}
 
export class AmbiguousStatement extends ABAPRule {
  private conf = new AmbiguousStatementConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "ambiguous_statement",
      title: "Check for ambigious statements",
      shortDescription: `Checks for ambiguity between deleting or modifying from internal and database table
Add "TABLE" keyword or "@" for escaping SQL variables
 
Only works if the target version is 740sp05 or above`,
      tags: [RuleTag.SingleFile],
      badExample: `DELETE foo FROM bar.
MODIFY foo FROM bar.`,
      goodExample: `DELETE foo FROM @bar.
DELETE TABLE itab FROM 2.
MODIFY zfoo FROM @wa.
MODIFY TABLE foo FROM bar.`,
    };
  }
 
  private getMessage(): string {
    return "Ambiguous statement. Use explicit syntax.";
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: AmbiguousStatementConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile) {
    const issues: Issue[] = [];
 
    if (!releaseAtLeast(this.reg.getConfig().getRelease(), Release.v740sp05)) {
      return [];
    }
 
    for (const statement of file.getStatements()) {
      let match = false;
 
      if (statement.get() instanceof Statements.DeleteDatabase) {
        match = this.tryMatch(statement, this.reg, Statements.DeleteInternal);
      } else if (statement.get() instanceof Statements.DeleteInternal) {
        match = this.tryMatch(statement, this.reg, Statements.DeleteDatabase);
      } else if (statement.get() instanceof Statements.ModifyInternal) {
        match = this.tryMatch(statement, this.reg, Statements.ModifyDatabase) || this.isBareStatement(statement);
      } else if (statement.get() instanceof Statements.ModifyDatabase) {
        match = this.tryMatch(statement, this.reg, Statements.ModifyInternal);
      } else if (statement.get() instanceof Statements.InsertDatabase) {
        match = this.isBareStatement(statement);
      } else if (statement.get() instanceof Statements.UpdateDatabase) {
        match = this.isBareStatement(statement);
      }
 
      if (match) {
        const issue = Issue.atStatement(file, statement, this.getMessage(), this.getMetadata().key, this.conf.severity);
        issues.push(issue);
      }
 
    }
 
    return issues;
  }
 
  private isBareStatement(st: StatementNode): boolean {
    const tokens = st.getTokens().slice(0);
    tokens.pop();
    return tokens.length === 2;
  }
 
  private tryMatch(st: StatementNode, reg: IRegistry, type1: new () => IStatement): boolean {
    const ver = reg.getConfig().getVersion();
 
    const tokens = st.getTokens().slice(0);
    tokens.pop();
 
    const match = Combi.run(new type1().getMatcher(), tokens, ver);
 
    return match !== undefined;
  }
 
}