Skip to content

Commit 9dac6c4

Browse files
fix
1 parent a1fd512 commit 9dac6c4

5 files changed

Lines changed: 118 additions & 48 deletions

File tree

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/checker.ts

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,46 @@ export class SyntaxChecker {
110110
}
111111

112112
/**
113-
* Check if a parameter is a function result parameter
113+
* Check if the syntax declares a return type
114+
* @param syntax - Command syntax
115+
* @returns True if syntax has a return type declaration (e.g., ") : Type")
116+
*/
117+
private syntaxHasReturnType(syntax: string): boolean {
118+
// Check for return type pattern: ) : Type at the end
119+
// This matches patterns like:
120+
// - "functionName ( params ) : ReturnType"
121+
// - "functionName ( params ) : cs.ViewPro.TableTheme"
122+
// - "functionName ( params ) : 4D.Object"
123+
// Type can be: simple (Text), namespaced (cs.Class), or dotted (4D.Object, cs.ViewPro.TableTheme)
124+
return /\)\s*:\s*[\w.]+\s*$/.test(syntax.trim());
125+
}
126+
127+
/**
128+
* Check if a parameter represents the function's return value
129+
* This is used to exclude function results from regular parameter validation
114130
* @param param - Parameter to check
115-
* @returns True if parameter is a function result (Result or Function result with Direction.Return)
131+
* @param syntax - Command syntax to check if it declares a return type
132+
* @returns True if parameter is a function result that should be excluded from parameter validation
116133
*/
117-
private isFunctionResult(param: DocumentationParameter): boolean {
118-
const name = param.name.toLowerCase();
134+
private isFunctionResult(param: DocumentationParameter, syntax?: string): boolean {
119135
const direction = param.direction;
120-
return direction === Direction.Return &&
121-
(name === 'result' || name === 'function result');
136+
137+
// Must be a Return direction parameter
138+
if (direction !== Direction.Return) {
139+
return false;
140+
}
141+
142+
// If no syntax provided, cannot determine - default to not excluding it
143+
if (!syntax) {
144+
return false;
145+
}
146+
147+
// A Return-direction parameter is a function result if:
148+
// The syntax declares a return type (e.g., "functionName(...) : Type")
149+
// This structurally distinguishes:
150+
// - Functions: "Abs(x : Real) : Real" - has return type, Return param is function result
151+
// - Commands with output: "EXECUTE METHOD(name : Text ; result : Variable)" - no return type, Return param is regular output
152+
return this.syntaxHasReturnType(syntax);
122153
}
123154

124155
/**
@@ -140,9 +171,10 @@ export class SyntaxChecker {
140171
/**
141172
* Get input parameter names (excluding return parameters)
142173
* @param params - Parameter array from documentation
174+
* @param syntax - Command syntax to determine if it's a function
143175
* @returns Array of input parameter names
144176
*/
145-
getInputParameterNames(params: DocumentationParameter[]): string[] {
177+
getInputParameterNames(params: DocumentationParameter[], syntax?: string): string[] {
146178
if (!params || params.length === 0) return [];
147179

148180
return params
@@ -153,7 +185,7 @@ export class SyntaxChecker {
153185
// But include other Direction.Return parameters as they are output parameters, not function results
154186
return (
155187
direction !== undefined &&
156-
!this.isFunctionResult(param)
188+
!this.isFunctionResult(param, syntax)
157189
);
158190
})
159191
.map(param => param.name.toLowerCase());
@@ -164,9 +196,10 @@ export class SyntaxChecker {
164196
* @param variant - Parsed variant object
165197
* @param params - Actual parameter array
166198
* @param actualParamNames - Array of actual parameter names
199+
* @param syntax - Command syntax to determine if it's a function
167200
* @returns Validation result with extraParams and typeMismatches
168201
*/
169-
validateVariantParameters(variant: ParsedVariant, params: DocumentationParameter[], actualParamNames: string[]): ValidationResult {
202+
validateVariantParameters(variant: ParsedVariant, params: DocumentationParameter[], actualParamNames: string[], syntax?: string): ValidationResult {
170203
const parsedParamNames = variant.parameters
171204
.filter(p => p.spread === -1) // Don't validate spread parameters
172205
.map(p => p.name.toLowerCase());
@@ -179,10 +212,10 @@ export class SyntaxChecker {
179212
);
180213

181214
// Check for type mismatches
182-
const typeMismatches = this.checkTypeMismatches(variant, params);
215+
const typeMismatches = this.checkTypeMismatches(variant, params, syntax);
183216

184217
// Check for return type mismatches
185-
const returnTypeMismatches = this.checkReturnTypeMismatches(variant, params);
218+
const returnTypeMismatches = this.checkReturnTypeMismatches(variant, params, syntax);
186219

187220
return { extraParams, typeMismatches, returnTypeMismatches };
188221
}
@@ -191,9 +224,10 @@ export class SyntaxChecker {
191224
* Check for type mismatches between parsed and actual parameters
192225
* @param variant - Parsed variant object
193226
* @param params - Actual parameter array
227+
* @param syntax - Command syntax to determine if it's a function
194228
* @returns Array of type mismatches
195229
*/
196-
checkTypeMismatches(variant: ParsedVariant, params: DocumentationParameter[]): TypeMismatch[] {
230+
checkTypeMismatches(variant: ParsedVariant, params: DocumentationParameter[], syntax?: string): TypeMismatch[] {
197231
const typeMismatches: TypeMismatch[] = [];
198232

199233
variant.parameters.forEach(parsedParam => {
@@ -205,7 +239,7 @@ export class SyntaxChecker {
205239
const parsedName = parsedParam.name.toLowerCase();
206240

207241
// Match by name but exclude function result parameters
208-
return paramName === parsedName && !this.isFunctionResult(p);
242+
return paramName === parsedName && !this.isFunctionResult(p, syntax);
209243
});
210244

211245
if (actualParam && parsedParam.type !== 'unknown') {
@@ -230,28 +264,37 @@ export class SyntaxChecker {
230264
* Check for return type mismatches between parsed and actual parameters
231265
* @param variant - Parsed variant object
232266
* @param params - Actual parameter array
267+
* @param syntax - Command syntax to determine if it declares a return type
233268
* @returns Array of return type mismatches
234269
*/
235-
checkReturnTypeMismatches(variant: ParsedVariant, params: DocumentationParameter[]): TypeMismatch[] {
270+
checkReturnTypeMismatches(variant: ParsedVariant, params: DocumentationParameter[], syntax?: string): TypeMismatch[] {
236271
const returnTypeMismatches: TypeMismatch[] = [];
237272

238273
// Check if the parsed variant has return type information
239274
if (!variant.returnType) {
240275
return returnTypeMismatches;
241276
}
242277

243-
// Find the actual return type parameter (Result, Function result, or by specific name)
278+
// Find the actual return type parameter
279+
// If syntax declares a return type, look for ANY parameter with Return direction
280+
// Otherwise, match by specific name if provided in the variant
244281
const actualReturnParam = params.find(p => {
245282
const direction = p.direction;
246283
const name = p.name.toLowerCase();
247284

248-
// Check if it's the function result by name and direction
249-
if (this.isFunctionResult(p)) {
285+
// Not a return direction parameter
286+
if (direction !== Direction.Return) {
287+
return false;
288+
}
289+
290+
// If syntax declares a return type, any Return parameter matches
291+
if (syntax && this.syntaxHasReturnType(syntax)) {
250292
return true;
251293
}
252294

253-
// If variant has a specific return name, match by name and direction
254-
if (variant.returnType!.name && direction === Direction.Return &&
295+
// Otherwise, match by specific name if variant provides one
296+
// This handles edge cases like named output parameters
297+
if (variant.returnType!.name &&
255298
name === variant.returnType!.name.toLowerCase()) {
256299
return true;
257300
}
@@ -275,7 +318,7 @@ export class SyntaxChecker {
275318

276319
if (!this.isTypeValid(syntaxType, actualType)) {
277320
returnTypeMismatches.push({
278-
name: variant.returnType!.name || 'Function result',
321+
name: variant.returnType!.name || actualReturnParam.name,
279322
syntaxType: syntaxType,
280323
paramsType: actualType
281324
});
@@ -340,9 +383,10 @@ export class SyntaxChecker {
340383
* @param variant - Parsed variant object
341384
* @param params - Actual parameter array (can be empty/undefined)
342385
* @param actualParamNames - Array of actual parameter names (can be empty)
386+
* @param syntax - Command syntax to determine if it's a function
343387
* @returns True if variant has any issues at current warning level
344388
*/
345-
private hasVariantIssues(variant: ParsedVariant, params: DocumentationParameter[] = [], actualParamNames: string[] = []): boolean {
389+
private hasVariantIssues(variant: ParsedVariant, params: DocumentationParameter[] = [], actualParamNames: string[] = [], syntax?: string): boolean {
346390
// Always check for malformations first
347391
const hasMalformation = this.hasRelevantMalformation(variant);
348392

@@ -351,7 +395,7 @@ export class SyntaxChecker {
351395
return hasMalformation;
352396
}
353397

354-
const { extraParams, typeMismatches, returnTypeMismatches } = this.validateVariantParameters(variant, params, actualParamNames);
398+
const { extraParams, typeMismatches, returnTypeMismatches } = this.validateVariantParameters(variant, params, actualParamNames, syntax);
355399
const hasParameterErrors = extraParams.length > 0 || typeMismatches.length > 0 || returnTypeMismatches.length > 0;
356400

357401
return hasMalformation || hasParameterErrors;
@@ -363,8 +407,9 @@ export class SyntaxChecker {
363407
* @param index - Variant index
364408
* @param params - Actual parameter array
365409
* @param actualParamNames - Array of actual parameter names
410+
* @param syntax - Command syntax to determine if it's a function
366411
*/
367-
outputVariantAnalysis(variant: ParsedVariant, index: number, params: DocumentationParameter[], actualParamNames: string[]): void {
412+
outputVariantAnalysis(variant: ParsedVariant, index: number, params: DocumentationParameter[], actualParamNames: string[], syntax?: string): void {
368413
console.log(`\nVariant ${index + 1} analysis:`);
369414
const parsedParamNames = variant.parameters.map(p => p.name.toLowerCase());
370415
console.log('Parsed parameter names:', parsedParamNames);
@@ -379,7 +424,7 @@ export class SyntaxChecker {
379424
});
380425
}
381426

382-
const { extraParams, typeMismatches, returnTypeMismatches } = this.validateVariantParameters(variant, params, actualParamNames);
427+
const { extraParams, typeMismatches, returnTypeMismatches } = this.validateVariantParameters(variant, params, actualParamNames, syntax);
383428

384429
if (extraParams.length > 0) {
385430
console.log(`⚠️ Extra/Invalid parameters: ${extraParams.join(', ')}`);
@@ -420,12 +465,12 @@ export class SyntaxChecker {
420465

421466
// Check if there are any errors/warnings
422467
let hasErrors = false;
423-
const actualParamNames = params && params.length > 0 ? this.getInputParameterNames(params) : [];
468+
const actualParamNames = params && params.length > 0 ? this.getInputParameterNames(params, syntax) : [];
424469
const allParamInfo = params && params.length > 0 ? this.extractActualParamNames(params) : [];
425470

426471
// Check each parsed variant for issues (malformations and parameter errors)
427472
parsedParams.forEach((variant) => {
428-
if (this.hasVariantIssues(variant, params, actualParamNames)) {
473+
if (this.hasVariantIssues(variant, params, actualParamNames, syntax)) {
429474
hasErrors = true;
430475
}
431476
});
@@ -444,7 +489,7 @@ export class SyntaxChecker {
444489

445490
// Check each parsed variant
446491
parsedParams.forEach((variant, index) => {
447-
this.outputVariantAnalysis(variant, index, params, actualParamNames);
492+
this.outputVariantAnalysis(variant, index, params, actualParamNames, syntax);
448493
});
449494
} else {
450495
console.log('\nNo Params field found for this command');

src/malformation-checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { WarningCode, WARNING_DEFINITIONS, MalformationIssue } from './types.js'
44
const validTypes = [
55
'object', 'text', 'real', 'any', 'integer', 'collection', 'date', 'time', 'boolean', 'picture', 'blob', 'variant', 'pointer',
66
'text array', 'object array', 'boolean array', 'integer array', 'real array', 'time array', 'pointer array', 'array', 'picture array', 'date array',
7-
'expression', 'table', 'field', 'variable', 'expression', 'operator', 'null', 'undefined'
7+
'expression', 'table', 'field', 'variable', 'expression', 'operator', 'null', 'undefined', 'comparator'
88
];
99

1010

0 commit comments

Comments
 (0)