Skip to content

Commit a9ccd8a

Browse files
authored
Merge pull request #404 from codefori/worksofliam/issue402
Support case check on subfields
2 parents 2173e92 + 08e20d0 commit a9ccd8a

File tree

3 files changed

+159
-113
lines changed

3 files changed

+159
-113
lines changed

language/linter.ts

Lines changed: 74 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@ export default class Linter {
171171
const currentProcedure = globalScope.procedures.find(proc => proc.position.path === data.uri && lineNumber >= proc.range.start && lineNumber <= proc.range.end);
172172
const currentScope = globalScope.merge(inProcedure && currentProcedure ? currentProcedure.scope : undefined);
173173

174-
// Only fetch the names if we have a rule that requires it. It might be slow.
175-
const definedNames = (rules.IncorrectVariableCase || rules.SQLHostVarCheck ? currentScope.getNames() : []);
176-
177174
let value;
178175
let isEmbeddedSQL = false;
179176

@@ -699,7 +696,7 @@ export default class Linter {
699696

700697
if (rules.SQLHostVarCheck) {
701698
statement.forEach((part, index) => {
702-
if (part.type === `word` && definedNames.some(name => name.toUpperCase() === part.value.toUpperCase())) {
699+
if (part.type === `word` && currentScope.findDefinition(lineNumber, part.value)) {
703700
const prior = statement[index - 1];
704701
if (prior && ![`dot`, `seperator`].includes(prior.type)) {
705702
errors.push({
@@ -821,24 +818,6 @@ export default class Linter {
821818

822819
const isDeclare = [`declare`, `end`].includes(statement[0].type);
823820

824-
if (rules.IncorrectVariableCase) {
825-
// Check the casing of the reference matches the definition
826-
if ((isEmbeddedSQL === false || (isEmbeddedSQL && statement[i - 1] && statement[i - 1].type === `seperator`))) {
827-
const possibleKeyword = (isDeclare || inPrototype || inStruct.length > 0) && i >= 0 && statement[i + 1] && statement[i + 1].type === `openbracket`;
828-
829-
if (!possibleKeyword && !isDirective) {
830-
const definedName = definedNames.find(defName => defName.toUpperCase() === upperName);
831-
if (definedName && definedName !== part.value) {
832-
errors.push({
833-
offset: part.range,
834-
type: `IncorrectVariableCase`,
835-
newValue: definedName
836-
});
837-
}
838-
}
839-
}
840-
}
841-
842821
if ((isDeclare && i >= 2) || !isDeclare) {
843822
if (rules.RequiresParameter && !inPrototype && !isDeclare) {
844823
// Check the procedure reference has a block following it
@@ -1026,102 +1005,112 @@ export default class Linter {
10261005
});
10271006
}
10281007

1029-
if (rules.NoUnreferenced) {
1008+
if (rules.NoUnreferenced || rules.IncorrectVariableCase) {
1009+
const noRefCheck = (def: Declaration) => {
1010+
if (def.name !== undefined && def.name.toUpperCase() !== NO_NAME && def.position.path === data.uri) {
1011+
if (def.references.length <= 1) {
1012+
const possibleStatement = doc.getStatementByLine(def.position.range.line);
1013+
if (possibleStatement) {
1014+
errors.push({
1015+
type: `NoUnreferenced`,
1016+
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1017+
});
1018+
}
1019+
}
1020+
}
1021+
}
1022+
const casingCheck = (def: Declaration) => {
1023+
if (def.name !== undefined && def.name.toUpperCase() !== NO_NAME) {
1024+
def.references.forEach(ref => {
1025+
if (ref.uri === data.uri) { // We're only checking the active file
1026+
if (!errors.some(e => e.offset.start === ref.offset.start)) { //This is required because LIKEDS shares subfields
1027+
const contentPosition = data.content.substring(ref.offset.start, ref.offset.end);
1028+
if (contentPosition !== def.name) {
1029+
errors.push({
1030+
type: `IncorrectVariableCase`,
1031+
offset: { start: ref.offset.start, end: ref.offset.end },
1032+
newValue: def.name
1033+
});
1034+
}
1035+
}
1036+
}
1037+
});
1038+
}
1039+
}
1040+
10301041
[
10311042
globalScope,
10321043
...globalScope.procedures.filter(proc => proc.scope !== undefined).map(proc => proc.scope)
10331044
].forEach(dec => {
10341045
[...dec.constants, ...dec.variables]
1035-
.filter(def => def.position.path === data.uri)
10361046
.forEach(def => {
1037-
if (def.references.length <= 1) {
1038-
// Add an error to def
1039-
const possibleStatement = doc.getStatementByLine(def.position.range.line);
1040-
if (possibleStatement) {
1041-
errors.push({
1042-
type: `NoUnreferenced`,
1043-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1044-
});
1045-
}
1047+
if (rules.NoUnreferenced) {
1048+
noRefCheck(def);
1049+
}
1050+
1051+
if (rules.IncorrectVariableCase) {
1052+
casingCheck(def);
10461053
}
10471054
});
10481055

10491056
dec.subroutines
1050-
.filter(def => def.position.path === data.uri && def.name && ![`*INZSR`, `*PSSR`].includes(def.name.toUpperCase()))
1057+
.filter(def => def.name && ![`*INZSR`, `*PSSR`].includes(def.name.toUpperCase()))
10511058
.forEach(def => {
1052-
if (def.references.length <= 1) {
1053-
// Add an error to def
1054-
const possibleStatement = doc.getStatementByLine(def.position.range.line);
1055-
if (possibleStatement) {
1056-
errors.push({
1057-
type: `NoUnreferenced`,
1058-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1059-
});
1060-
}
1059+
if (rules.NoUnreferenced) {
1060+
noRefCheck(def);
1061+
}
1062+
1063+
if (rules.IncorrectVariableCase) {
1064+
casingCheck(def);
10611065
}
10621066
});
10631067

10641068
dec.procedures
1065-
.filter(struct => struct.position.path === data.uri)
10661069
.forEach(proc => {
10671070
if (!proc.keyword[`EXPORT`]) {
1068-
if (proc.references.length <= 1) {
1069-
// Add an error to proc
1070-
const possibleStatement = doc.getStatementByLine(proc.position.range.line);
1071-
if (possibleStatement) {
1072-
errors.push({
1073-
type: `NoUnreferenced`,
1074-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1075-
});
1076-
}
1071+
if (rules.NoUnreferenced) {
1072+
noRefCheck(proc);
1073+
}
1074+
1075+
if (rules.IncorrectVariableCase) {
1076+
casingCheck(proc);
10771077
}
10781078

10791079
if (!proc.keyword[`EXTPGM`] && !proc.keyword[`EXTPROC`]) {
10801080
proc.subItems.forEach(parm => {
1081-
if (parm.references.length <= 1) {
1082-
const possibleStatement = doc.getStatementByLine(parm.position.range.line);
1083-
if (possibleStatement) {
1084-
errors.push({
1085-
type: `NoUnreferenced`,
1086-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1087-
});
1088-
}
1081+
if (rules.NoUnreferenced) {
1082+
noRefCheck(parm);
1083+
}
1084+
1085+
if (rules.IncorrectVariableCase) {
1086+
casingCheck(parm);
10891087
}
10901088
});
10911089
}
10921090
}
10931091
});
10941092

10951093
dec.structs
1096-
.filter(struct => struct.position.path === data.uri)
10971094
.forEach(struct => {
1098-
const subFieldIsUsed = struct.subItems.some(subf => subf.references.length > 1);
10991095

1100-
if (struct.references.length <= 1) {
1096+
struct.subItems.forEach(subf => {
11011097
// We only check the subfields if the parent is never references.
1098+
if (rules.NoUnreferenced && struct.references.length <= 1) {
1099+
noRefCheck(subf);
1100+
}
11021101

1103-
struct.subItems.forEach(subf => {
1104-
if (subf.name !== NO_NAME && subf.references.length <= 1) {
1105-
// Add an error to subf
1106-
const possibleStatement = doc.getStatementByLine(subf.position.range.line);
1107-
if (possibleStatement) {
1108-
errors.push({
1109-
type: `NoUnreferenced`,
1110-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1111-
});
1112-
}
1113-
}
1114-
});
1115-
1116-
if (subFieldIsUsed === false) {
1117-
const possibleStatement = doc.getStatementByLine(struct.position.range.line);
1118-
if (possibleStatement) {
1119-
errors.push({
1120-
type: `NoUnreferenced`,
1121-
offset: { start: possibleStatement.range.start, end: possibleStatement.range.end }
1122-
});
1123-
}
1102+
if (rules.IncorrectVariableCase) {
1103+
casingCheck(subf);
11241104
}
1105+
});
1106+
1107+
const subFieldIsUsed = struct.subItems.some(subf => subf.references.length > 1);
1108+
if (rules.NoUnreferenced && subFieldIsUsed === false) {
1109+
noRefCheck(struct);
1110+
}
1111+
1112+
if (rules.IncorrectVariableCase) {
1113+
casingCheck(struct);
11251114
}
11261115
});
11271116
});

tests/suite/directives.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ test('skip3', async () => {
139139
`return`,
140140
].join(`\n`);
141141

142-
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true });
142+
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true, collectReferences: true });
143143
const { errors } = Linter.getErrors({ uri, content: lines }, {
144144
IncorrectVariableCase: true
145145
}, cache);
@@ -286,7 +286,7 @@ test('eof5_issue181', async () => {
286286
`End-Proc;`,
287287
].join(`\n`);
288288

289-
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true });
289+
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true, collectReferences: true });
290290
const { errors } = Linter.getErrors({ uri, content: lines }, {
291291
IncorrectVariableCase: true
292292
}, cache);
@@ -479,26 +479,26 @@ test('variable_case1', async () => {
479479
`Return;`
480480
].join(`\n`);
481481

482-
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true });
482+
const cache = await parser.getDocs(uri, lines, { withIncludes: true, ignoreCache: true, collectReferences: true });
483483
const { errors } = Linter.getErrors({ uri, content: lines }, {
484484
IncorrectVariableCase: true
485485
}, cache);
486486

487487
expect(errors.length).toBe(3);
488488

489-
expect(errors[0]).toMatchObject({
489+
expect(errors).toContainEqual({
490490
offset: { start: 86, end: 100 },
491491
type: `IncorrectVariableCase`,
492492
newValue: `CustomerName_t`
493493
});
494494

495-
expect(errors[1]).toMatchObject({
495+
expect(errors).toContainEqual({
496496
offset: { start: 174, end: 188 },
497497
type: `IncorrectVariableCase`,
498498
newValue: `CustomerName_t`
499499
});
500500

501-
expect(errors[2]).toMatchObject({
501+
expect(errors).toContainEqual({
502502
offset: { start: 218, end: 232 },
503503
type: `IncorrectVariableCase`,
504504
newValue: `CustomerName_t`

0 commit comments

Comments
 (0)