All files / src/rules unknown_types.ts

98.5% Statements 131/133
92.31% Branches 36/39
100% Functions 9/9
98.5% Lines 131/133

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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 1331x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7653x 7653x 7653x 7653x 7653x 22825x 22825x 22825x 22825x 22825x 22825x 22825x 7653x 7653x 147x 147x 147x 7653x 7653x 7346x 7346x 7653x 7653x 139x 139x 7653x 7653x 173x 20x 20x 153x 153x 153x 153x 153x 7653x 7653x 7653x 7653x 648x 648x 648x 209x 1x 1x 1x 209x 648x 648x 572x 572x 4085x 4085x 4085x 8x 8x 8x 4085x 572x 572x 572x 504x 504x 504x 1x 1x 1x 504x 572x 648x 648x 30x 30x 1x 1x 1x 30x 648x 648x 495x 495x 648x 648x 648x 7653x 7653x 30x 7x 6x 6x 1x 1x 6x 6x 29x 29x 7653x 7653x 56696x 10x 56696x 644x 52087x 52087x     52087x 56686x 14x 14x 56672x 56672x 7653x 7653x
import {IRegistry} from "../_iregistry";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IObject} from "../objects/_iobject";
import {ABAPObject} from "../objects/_abap_object";
import {Issue} from "../issue";
import * as BasicTypes from "../abap/types/basic";
import {IRuleMetadata, RuleTag, IRule} from "./_irule";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {AbstractType} from "../abap/types/basic/_abstract_type";
import {TypedIdentifier} from "../abap/types/_typed_identifier";
import {IInterfaceDefinition} from "../abap/types/_interface_definition";
import {Identifier} from "../abap/4_file_information/_identifier";
import {ScopeType} from "../abap/5_syntax/_scope_type";
import {ReferenceType} from "../abap/5_syntax/_reference";
 
export class UnknownTypesConf extends BasicRuleConfig {
}
 
export class UnknownTypes implements IRule {
  private reg: IRegistry;
  private conf = new UnknownTypesConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "unknown_types",
      title: "Unknown types",
      shortDescription: `Enables check for unknown data types, respects errorNamespace`,
      tags: [RuleTag.Syntax],
    };
  }
 
  public initialize(reg: IRegistry) {
    this.reg = reg;
    return this;
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: UnknownTypesConf) {
    this.conf = conf;
  }
 
  public run(obj: IObject): Issue[] {
    if (!(obj instanceof ABAPObject)) {
      return [];
    }
 
    const spaghetti = new SyntaxLogic(this.reg, obj).run().spaghetti;
 
    return this.traverse(spaghetti.getTop());
  }
 
/////////////////////
 
  private traverse(node: ISpaghettiScopeNode): Issue[] {
    const ret: Issue[] = [];
 
    for (const r of node.getData().references) {
      if (r.referenceType === ReferenceType.ObjectOrientedUnknownReference && r.extra?.ooName) {
        const message = r.extra.ooName + " unknown";
        ret.push(Issue.atIdentifier(r.position, message, this.getMetadata().key, this.conf.severity));
      }
    }
 
    if (node.getIdentifier().stype !== ScopeType.ClassImplementation) {
      const vars = node.getData().vars;
      for (const name in vars) {
        const identifier = vars[name];
        const found = this.containsUnknown(identifier.getType());
        if (found) {
          const message = "Variable \"" + name + "\" contains unknown: " + found;
          ret.push(Issue.atIdentifier(identifier, message, this.getMetadata().key, this.conf.severity));
        }
      }
 
      const types = node.getData().types;
      for (const name in types) {
        const identifier = types[name];
        const found = this.containsUnknown(identifier.getType());
        if (found) {
          const message = "Type \"" + name + "\" contains unknown: " + found;
          ret.push(Issue.atIdentifier(identifier, message, this.getMetadata().key, this.conf.severity));
        }
      }
    }
 
    for (const v of node.getData().idefs) {
      const found = this.checkInterface(v);
      if (found) {
        const message = "Contains unknown, " + found.found;
        ret.push(Issue.atIdentifier(found.id, message, this.getMetadata().key, this.conf.severity));
      }
    }
 
    for (const n of node.getChildren()) {
      ret.push(...this.traverse(n));
    }
 
    return ret;
  }
 
  private checkInterface(idef: IInterfaceDefinition): {id: Identifier, found: string} | undefined {
    for (const m of idef.getMethodDefinitions()?.getAll() || []) {
      for (const p of m.getParameters().getAll()) {
        const found = this.containsUnknown(p.getType());
        if (found) {
          return {id: p, found};
        }
      }
    }
    return undefined;
  }
 
  private containsUnknown(type: AbstractType): string | undefined {
    if (type instanceof BasicTypes.UnknownType) {
      return type.getError();
    } else if (type instanceof BasicTypes.StructureType) {
      for (const c of type.getComponents()) {
        const found = this.containsUnknown(c.type instanceof TypedIdentifier ? c.type.getType() : c.type);
        if (found) {
          return found;
        }
      }
    } else if (type instanceof BasicTypes.TableType) {
      return this.containsUnknown(type.getRowType());
    }
    return undefined;
  }
 
}