All files / src/rules omit_parameter_name.ts

90.16% Statements 55/61
75% Branches 33/44
100% Functions 7/7
90.16% Lines 55/61

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 1281x 1x 1x   1x   1x 1x     1x 1x 1x   1x     1x   7276x     21675x                             122x 122x       7015x       114x       140x 140x 18x     122x   122x 126x 126x 12x     114x 8x       8x 8x 5x   3x 3x     3x 3x       3x 3x       3x 3x 1x   2x   2x 2x 2x 2x 2x 2x   2x         122x           3x 3x       3x 6x 3x 3x   3x              
import {Issue} from "../issue";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {IRegistry} from "../_iregistry";
import * as Expressions from "../abap/2_statements/expressions";
import {IObject} from "../objects/_iobject";
import {ABAPObject} from "../objects/_abap_object";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {Token} from "../abap/1_lexer/tokens/_token";
import {ISpaghettiScope} from "../abap/5_syntax/_spaghetti_scope";
import {ReferenceType} from "../abap/5_syntax/_reference";
import {MethodDefinition} from "../abap/types/method_definition";
import {EditHelper, IEdit} from "../edit_helper";
 
export class OmitParameterNameConf extends BasicRuleConfig {
}
 
export class OmitParameterName implements IRule {
  private reg: IRegistry;
  private conf = new OmitParameterNameConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "omit_parameter_name",
      title: "Omit parameter name",
      shortDescription: `Omit the parameter name in single parameter calls`,
      extendedInformation: `
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#omit-the-parameter-name-in-single-parameter-calls
 
EXPORTING must already be omitted for this rule to take effect, https://rules.abaplint.org/exporting/`,
      tags: [RuleTag.Styleguide, RuleTag.Quickfix],
      badExample: `method( param = 2 ).`,
      goodExample: `method( 2 ).`,
    };
  }
 
  public initialize(reg: IRegistry) {
    this.reg = reg;
    return this;
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: OmitParameterNameConf) {
    this.conf = conf;
  }
 
  public run(obj: IObject): Issue[] {
    const issues: Issue[] = [];
    if (!(obj instanceof ABAPObject) || obj.getType() === "INTF") {
      return [];
    }
 
    const spaghetti = new SyntaxLogic(this.reg, obj).run().spaghetti;
 
    for (const file of obj.getABAPFiles()) {
      const stru = file.getStructure();
      if (stru === undefined) {
        continue;
      }
 
      for (const c of stru.findAllExpressions(Expressions.MethodCall)) {
        Iif (c.findFirstExpression(Expressions.MethodParameters)) {
          continue;
        }
        // hmm, this will break for nested method calls?
        const parameters = c.findAllExpressions(Expressions.ParameterS);
        if (parameters.length > 1 || parameters.length === 0) {
          continue;
        }
        const name = c.findDirectExpression(Expressions.MethodName);
        Iif (name === undefined) {
          continue;
        }
        const param = c.findDirectExpression(Expressions.MethodCallParam);
        Iif (param === undefined) {
          continue;
        }
 
        const ref = this.findMethodReference(name.getFirstToken(), spaghetti, file.getFilename());
        Iif (ref === undefined) {
          continue;
        }
 
        const i = ref.getParameters().getDefaultImporting();
        if (i === undefined) {
          continue;
        }
        const p = parameters[0].findDirectExpression(Expressions.ParameterName)?.getFirstToken();
 
        Eif (p?.getStr().toUpperCase() === i.toUpperCase()) {
          const message = "Omit default parameter name \"" + i + "\"";
          let fix: IEdit | undefined = undefined;
          const end = parameters[0].findDirectExpression(Expressions.Source)?.getFirstToken().getStart();
          Eif (end) {
            fix = EditHelper.deleteRange(file, p.getStart(), end);
          }
          issues.push(Issue.atToken(file, name.getFirstToken(), message, this.getMetadata().key, this.getConfig().severity, fix));
        }
      }
    }
 
    return issues;
  }
 
///////////////////
 
  private findMethodReference(token: Token, spaghetti: ISpaghettiScope, filename: string): undefined | MethodDefinition {
    const scope = spaghetti.lookupPosition(token.getStart(), filename);
    Iif (scope === undefined) {
      return undefined;
    }
 
    for (const r of scope.getData().references) {
      if (r.referenceType !== ReferenceType.MethodReference) {
        continue;
      } else Eif (r.position.getStart().equals(token.getStart())
          && r.resolved instanceof MethodDefinition) {
        return r.resolved;
      }
    }
 
    return undefined;
  }
 
}