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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14704x 14704x 14704x 14704x 14704x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 43917x 14704x 14704x 14120x 14120x 14704x 14704x 264x 264x 14704x 14704x 314x 314x 314x 314x 1837x 1837x 1743x 1743x 94x 94x 1837x 94x 94x 94x 1837x 42x 42x 42x 3x 3x 42x 42x 5x 5x 34x 34x 34x 1837x 76x 76x 18x 18x 18x 18x 18x 1837x 34x 34x 7x 7x 27x 27x 34x 9x 34x 1x 1x 27x 34x 10x 10x 34x 18x 1837x 1837x 1837x 14704x 14704x 14704x 39x 2x 2x 37x 39x 39x 14704x 14704x | import * as Statements from "../abap/2_statements/statements";
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {Comment} from "../abap/2_statements/statements/_statement";
import {StatementNode} from "../abap/nodes";
export class AbapdocParameterNamesConf extends BasicRuleConfig {
}
export class AbapdocParameterNames extends ABAPRule {
private conf = new AbapdocParameterNamesConf();
public getMetadata(): IRuleMetadata {
return {
key: "abapdoc_parameter_names",
title: "ABAP Doc parameter names",
shortDescription: `Checks that the parameters documented in ABAP Doc exist in the method definition.`,
extendedInformation: `Reports "! @parameter lines in the ABAP Doc block in front of a METHODS or CLASS-METHODS
definition when the parameter is not a parameter of the method, typically after a parameter was renamed or removed.
Also reports a parameter documented twice.
Missing @parameter lines are not reported, documenting parameters is optional. @raising and @exception lines are not checked.
Only the block directly in front of the method is checked, a blank line or a plain comment ends the block,
see rule wrong_abapdoc_position for misplaced ABAP Doc.`,
tags: [RuleTag.SingleFile],
badExample: `CLASS zcl_foo DEFINITION PUBLIC.
PUBLIC SECTION.
"! @parameter iv_bar | Bar
METHODS foo IMPORTING iv_foo TYPE i.
ENDCLASS.`,
goodExample: `CLASS zcl_foo DEFINITION PUBLIC.
PUBLIC SECTION.
"! @parameter iv_foo | Foo
METHODS foo IMPORTING iv_foo TYPE i.
ENDCLASS.`,
};
}
public getConfig() {
return this.conf;
}
public setConfig(conf: AbapdocParameterNamesConf) {
this.conf = conf;
}
public runParsed(file: ABAPFile): Issue[] {
const issues: Issue[] = [];
const statements = file.getStatements();
for (let i = 0; i < statements.length; i++) {
const def = statements[i];
if (!(def.get() instanceof Statements.MethodDef)) {
continue;
}
const name = def.findDirectExpression(Expressions.MethodName);
if (name === undefined) {
continue;
}
const abapdoc: StatementNode[] = [];
let nextRow: number | undefined = undefined;
for (let j = i - 1; j >= 0 && statements[j].get() instanceof Comment; j--) {
const comment = statements[j];
const start = comment.getStart();
if (start.isAfter(name.getFirstToken().getStart())) {
continue; // comment inside the statement, after the method name
}
nextRow = nextRow ?? def.getTokens().find(t => t.getStart().isAfter(start))?.getStart().getRow();
if (this.isAbapdoc(comment, def) === false || start.getRow() + 1 !== nextRow) {
break; // a blank line or a plain comment ends the block
}
abapdoc.unshift(comment);
nextRow = start.getRow();
}
if (abapdoc.length === 0) {
continue;
}
const parameters = def.findAllExpressions(Expressions.MethodParamName)
.map(p => p.getFirstToken().getStr().replace(/^!/, "").toUpperCase());
const documented = new Set<string>();
for (const comment of abapdoc) {
const match = comment.getFirstToken().getStr().match(/^"!\s*@parameter\s+!?([^\s|]+)/i);
if (match === null) {
continue;
}
const parameter = match[1].toUpperCase();
let message: string | undefined = undefined;
if (parameters.includes(parameter) === false) {
message = `ABAP Doc parameter "${match[1]}" is not a parameter of method ${name.concatTokens()}`;
} else if (documented.has(parameter) === true) {
message = `ABAP Doc parameter "${match[1]}" documented more than once`;
}
documented.add(parameter);
if (message !== undefined) {
issues.push(Issue.atStatement(file, comment, message, this.getMetadata().key, this.conf.severity));
}
}
}
return issues;
}
/** ABAP Doc in front of the method name, after the colon of a chain */
private isAbapdoc(comment: StatementNode, def: StatementNode): boolean {
if (comment.getFirstToken().getStr().startsWith(`"!`) === false) {
return false;
}
const colon = def.getColon();
return colon === undefined || comment.getStart().isAfter(colon.getStart());
}
}
|