All files / src/rules clear_after_declaration.ts

100% Statements 82/82
100% Branches 21/21
100% Functions 6/6
100% Lines 82/82

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 831x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14725x 14725x 14725x 14725x 14725x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 114590x 14725x 14725x 14120x 14120x 14725x 14725x 264x 264x 14725x 14725x 412x 412x 412x 14725x 14725x 509x 509x 116x 116x 393x 393x 509x 1634x 1x 1x 1634x 225x 225x 225x 225x 225x 169x 169x 56x 56x 56x 56x 56x 1633x 393x 393x 14725x  
import {Issue} from "../issue";
import {IObject} from "../objects/_iobject";
import {IRegistry} from "../_iregistry";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {ObjectAnalysis, OccurrenceKind, deleteStatement, initialAtFirstReference, plainClear} from "./_procedure_analysis";
 
export class ClearAfterDeclarationConf extends BasicRuleConfig {
}
 
export class ClearAfterDeclaration implements IRule {
  private reg: IRegistry;
  private conf = new ClearAfterDeclarationConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "clear_after_declaration",
      title: "CLEAR after declaration",
      shortDescription: `Finds CLEAR of a local variable that is still initial since its declaration`,
      extendedInformation: `Reports CLEAR x. when all of the following hold:
 
* x is a local variable of a METHOD, FORM or FUNCTION, declared with DATA x TYPE/LIKE ... without VALUE,
  or with DATA(x) = VALUE t( ). with an explicit type and nothing in the parentheses
* the CLEAR is the first reference to x after the declaration, it has no WITH addition
* neither the declaration nor the CLEAR is inside a LOOP, DO, WHILE, SELECT loop, PROVIDE, or a TRY block with
  RETRY, RESUME or CATCH BEFORE UNWIND. DATA is not executed per iteration, so a CLEAR in a loop can be needed.
  DATA(x) = VALUE t( ). is executed per iteration, it may also be in the same loop as the CLEAR, if the CLEAR is
  in the block of the declaration or a nested block
 
Any later use of x does not matter, the CLEAR can only be reached with x still initial.
Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
INCLUDE, test seams, enhancements or native SQL.
 
The quick fix deletes the CLEAR. No release requirement.`,
      tags: [RuleTag.Quickfix],
      badExample: `DATA lv_count TYPE i.
CLEAR lv_count.`,
      goodExample: `DATA lv_count TYPE i.`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: ClearAfterDeclarationConf) {
    this.conf = conf;
  }
 
  public initialize(reg: IRegistry) {
    this.reg = reg;
    return this;
  }
 
  public run(obj: IObject): readonly Issue[] {
    const analysis = ObjectAnalysis.run(this.reg, obj);
    if (analysis === undefined) {
      return [];
    }
 
    const issues: Issue[] = [];
    for (const procedure of analysis.procedures) {
      if (procedure.supported === false) {
        continue;
      }
      for (const variable of procedure.locals()) {
        const first = variable.occurrences[0];
        if (first === undefined
            || first.kind !== OccurrenceKind.Write
            || plainClear(first.statement)?.getStart().equals(first.token.getStart()) !== true
            || !initialAtFirstReference(procedure, variable, first.statement)) {
          continue;
        }
        const fix = deleteStatement(procedure.file, first.statement);
        issues.push(Issue.atStatement(procedure.file, first.statement,
                                      "CLEAR of " + variable.identifier.getName() + ", which is still initial",
                                      this.getMetadata().key, this.conf.severity, fix));
      }
    }
    return issues;
  }
}