Skip to content

Commit

Permalink
tsconfig option strictPropertyInitialization on meta/validator
Browse files Browse the repository at this point in the history
  • Loading branch information
annekekleppe committed Jun 26, 2024
1 parent dccabd8 commit 7ecb908
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 153 deletions.
8 changes: 6 additions & 2 deletions packages/meta/src/utils/generation/Names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ export class Names {
return this.startWithUpperCase(language?.name) + "Initialization";
}

public static concept(concept: FreMetaConcept): string {
return this.startWithUpperCase(concept?.name);
public static concept(concept: FreMetaConcept | undefined): string {
if (!!concept) {
return this.startWithUpperCase(concept?.name);
} else {
return "<unknown concept>";
}
}

public static classifier(concept: FreMetaClassifier): string {
Expand Down
8 changes: 4 additions & 4 deletions packages/meta/src/validatordef/generator/ValidationUtils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { FreMetaClassifier, FreMetaPrimitiveType } from "../../languagedef/metalanguage";
import {FreMetaClassifier, FreMetaPrimitiveProperty, FreMetaPrimitiveType} from "../../languagedef/metalanguage";

export class ValidationUtils {
public static findLocationDescription(concept: FreMetaClassifier): string {
let nameProp = concept.allPrimProperties().find(prop => prop.name === "name");
public static findLocationDescription(concept: FreMetaClassifier | undefined): string {
let nameProp: FreMetaPrimitiveProperty | undefined = concept?.allPrimProperties().find(prop => prop.name === "name");
if (!(!!nameProp)) {
nameProp = concept.allPrimProperties().find(prop => prop.type === FreMetaPrimitiveType.identifier);
nameProp = concept?.allPrimProperties().find(prop => prop.type === FreMetaPrimitiveType.identifier);
}
return !!nameProp ? `modelelement.${nameProp.name}` : `'unnamed'`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import { LOG2USER } from "../../utils/UserLogger";
const LOGGER = new MetaLogger("ValidatorGenerator").mute();
export class ValidatorGenerator {
public outputfolder: string = ".";
public language: FreMetaLanguage;
public language: FreMetaLanguage | undefined;
protected validatorGenFolder: string = '';
protected validatorFolder: string = '';

generate(validdef: ValidatorDef| undefined): void {
if (this.language === null) {
if (this.language === null || this.language === undefined) {
LOGGER.error("Cannot generate validator because language is not set.");
return;
}
Expand Down Expand Up @@ -109,15 +109,15 @@ export class ValidatorGenerator {
FileUtil.deleteDirAndContent(this.validatorGenFolder);
if (force) {
FileUtil.deleteFile(`${this.validatorFolder}/index.ts`);
if (this.language === null) {
if (this.language === null || this.language === undefined) {
LOG2USER.error("Cannot remove all because language is not set.");
} else {
FileUtil.deleteFile(`${this.validatorFolder}/${Names.customValidator(this.language)}.ts`);
}
FileUtil.deleteDirIfEmpty(this.validatorFolder);
} else {
// do not delete the following files, because these may contain user edits
LOG2USER.info(`${this.validatorFolder}/${Names.customValidator(this.language)}.ts` +
LOG2USER.info(`Not deleted: ${this.validatorFolder}/${!!this.language ? Names.customValidator(this.language) : "<Custom Validator>"}.ts` +
"\n\t" + `${this.validatorFolder}/index.ts`);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
CheckEqualsTypeRule,
ConceptRuleSet,
ExpressionRule,
IsuniqueRule,
IsUniqueRule,
NotEmptyRule,
ValidatorDef,
ValidationMessage,
Expand All @@ -29,13 +29,13 @@ import { ValidationUtils } from "../ValidationUtils";
export class RulesCheckerTemplate {

generateRulesChecker(language: FreMetaLanguage, validdef: ValidatorDef, relativePath: string): string {
const defaultWorkerName = Names.defaultWorker(language);
const defaultWorkerName: string = Names.defaultWorker(language);
const errorClassName: string = Names.FreError;
const checkerClassName: string = Names.rulesChecker(language);
const typerInterfaceName: string = Names.FreTyper;
const writerInterfaceName: string = Names.FreWriter;
const checkerInterfaceName: string = Names.checkerInterface(language);
const commentBefore = `/**
const commentBefore: string = `/**
* Checks 'modelelement' before checking its children.
* Found errors are pushed onto 'errorlist'.
* @param modelelement
Expand Down Expand Up @@ -64,7 +64,7 @@ export class RulesCheckerTemplate {
${validdef.conceptRules.map(ruleSet =>
`${commentBefore}
public execBefore${Names.concept(ruleSet.conceptRef.referred)}(modelelement: ${Names.concept(ruleSet.conceptRef.referred)}): boolean {
public execBefore${Names.concept(ruleSet.conceptRef?.referred)}(modelelement: ${Names.concept(ruleSet.conceptRef?.referred)}): boolean {
let hasFatalError: boolean = false;
${this.createRules(ruleSet)}
return hasFatalError;
Expand Down Expand Up @@ -105,14 +105,14 @@ export class RulesCheckerTemplate {
private createRules(ruleSet: ConceptRuleSet): string {
let result: string = "";
// find the property that indicates the location in human terms
const locationdescription = ValidationUtils.findLocationDescription(ruleSet.conceptRef.referred);
const locationdescription: string = ValidationUtils.findLocationDescription(ruleSet.conceptRef?.referred);

ruleSet.rules.forEach((r, index) => {
// find the severity for the rule
const severity: string = this.makeSeverity(r);

// if this rule has a message defined by the language engineer then use it
const message: string = this.makeMessage(r.message);
const message: string | undefined = !!r.message ? this.makeMessage(r.message) : undefined;

// add a comment to the result
result += `// ${r.toFreString()}\n`;
Expand All @@ -128,7 +128,7 @@ export class RulesCheckerTemplate {
result += this.makeValidNameRule(r, locationdescription, severity, message);
} else if (r instanceof ExpressionRule) {
result += this.makeExpressionRule(r, locationdescription, severity, message);
} else if (r instanceof IsuniqueRule) {
} else if (r instanceof IsUniqueRule) {
result += this.makeIsuniqueRule(r, locationdescription, severity, message);
}
});
Expand Down Expand Up @@ -164,89 +164,111 @@ export class RulesCheckerTemplate {
return result;
}

private makeExpressionRule(r: ExpressionRule, locationdescription: string, severity: string, message: string) {
if (message.length === 0) {
private makeExpressionRule(r: ExpressionRule, locationdescription: string, severity: string, message?: string) {
if (!message || message.length === 0) {
message = `"'${r.toFreString()}' is false"`;
}
return `if (!(${GenerationUtil.langExpToTypeScript(r.exp1)} ${r.comparator} ${GenerationUtil.langExpToTypeScript(r.exp2)})) {
if (!!r.exp1 && !!r.exp2) {
return `if (!(${GenerationUtil.langExpToTypeScript(r.exp1)} ${r.comparator} ${GenerationUtil.langExpToTypeScript(r.exp2)})) {
this.errorList.push( new ${Names.FreError}( ${message}, modelelement, ${locationdescription}, ${severity} ));
${r.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}`;
} else {
return "<error in makeExpressionRule>"
}
}

private makeValidNameRule(r: ValidNameRule, locationdescription: string, severity: string, message: string) {
if (message.length === 0) {
message = `"'" + ${GenerationUtil.langExpToTypeScript(r.property)} + "' is not a valid identifier"`;
}
return `if (!this.isValidName(${GenerationUtil.langExpToTypeScript(r.property)})) {
private makeValidNameRule(r: ValidNameRule, locationdescription: string, severity: string, message?: string): string {
if (!!r.property) {
if (!message || message.length === 0) {
message = `"'" + ${GenerationUtil.langExpToTypeScript(r.property)} + "' is not a valid identifier"`;
}
return `if (!this.isValidName(${GenerationUtil.langExpToTypeScript(r.property)})) {
this.errorList.push( new ${Names.FreError}( ${message}, modelelement, ${locationdescription}, ${severity} ));
${r.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}`;
} else {
return "<error in makeValidNameRule>"
}
}

private makeNotEmptyRule(r: NotEmptyRule, locationdescription: string, severity: string, message: string) {
if (message.length === 0) {
message = `"List '${r.property.toFreString()}' may not be empty"`;
}
return `if (${GenerationUtil.langExpToTypeScript(r.property)}.length === 0) {
private makeNotEmptyRule(r: NotEmptyRule, locationdescription: string, severity: string, message?: string): string {
if (!!r.property) {
if (!message || message.length === 0) {
message = `"List '${r.property.toFreString()}' may not be empty"`;
}
return `if (${GenerationUtil.langExpToTypeScript(r.property)}.length === 0) {
this.errorList.push(new ${Names.FreError}(${message}, modelelement, ${locationdescription}, "${r.property.toFreString()}", ${severity}));
${r.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}`;
} else {
return "<error in makeNotEmptyRule>"
}
}

private makeConformsRule(r: CheckConformsRule, locationdescription: string, severity: string, message?: string) {
if (!message || message.length === 0) {
message = `"Type " + this.typer.inferType(${GenerationUtil.langExpToTypeScript(r.type1)})?.toFreString(this.myWriter) + " of [" + this.myWriter.writeNameOnly(${GenerationUtil.langExpToTypeScript(r.type1)}) +
private makeConformsRule(r: CheckConformsRule, locationdescription: string, severity: string, message?: string): string {
if (!!r.type1 && !!r.type2) {
if (!message || message.length === 0) {
message = `"Type " + this.typer.inferType(${GenerationUtil.langExpToTypeScript(r.type1)})?.toFreString(this.myWriter) + " of [" + this.myWriter.writeNameOnly(${GenerationUtil.langExpToTypeScript(r.type1)}) +
"] does not conform to " + this.myWriter.writeNameOnly(${GenerationUtil.langExpToTypeScript(r.type2)})`;
}
return `if (!this.typer.conformsType(${GenerationUtil.langExpToTypeScript(r.type1)}, ${GenerationUtil.langExpToTypeScript(r.type2)})) {
}
return `if (!this.typer.conformsType(${GenerationUtil.langExpToTypeScript(r.type1)}, ${GenerationUtil.langExpToTypeScript(r.type2)})) {
this.errorList.push(new ${Names.FreError}(${message}, ${GenerationUtil.langExpToTypeScript(r.type1)}, ${locationdescription}, ${severity}));
${r.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}`;
} else {
return "<error in makeConformsRule>";
}

}

private makeEqualsTypeRule(r: CheckEqualsTypeRule, locationdescription: string, severity: string, index: number, message?: string) {
private makeEqualsTypeRule(r: CheckEqualsTypeRule, locationdescription: string, severity: string, index: number, message?: string): string {
// TODO change other methods similar to this one, i.e. first determine the types then call typer on types
// TODO make sure alle errors message use the same format
const leftElement: string = GenerationUtil.langExpToTypeScript(r.type1);
const rightElement: string = GenerationUtil.langExpToTypeScript(r.type2);
if (!message || message.length === 0) {
message = `"Type of '"+ this.myWriter.writeNameOnly(${leftElement})
if (!!r.type1 && !!r.type2) {
const leftElement: string = GenerationUtil.langExpToTypeScript(r.type1);
const rightElement: string = GenerationUtil.langExpToTypeScript(r.type2);
if (!message || message.length === 0) {
message = `"Type of '"+ this.myWriter.writeNameOnly(${leftElement})
+ "' (" + leftType${index}?.toFreString(this.myWriter) + ") should equal the type of '"
+ this.myWriter.writeNameOnly(${rightElement})
+ "' (" + rightType${index}?.toFreString(this.myWriter) + ")"`;
}
return `const leftType${index} = this.typer.inferType(${leftElement});
}
return `const leftType${index} = this.typer.inferType(${leftElement});
const rightType${index} = this.typer.inferType(${rightElement});
if (!this.typer.equals(leftType${index}, rightType${index})) {
this.errorList.push(new ${Names.FreError}(${message}, ${leftElement}, ${locationdescription}, ${severity}));
${r.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}`;
} else {
return "<error in makeEqualsTypeRule>";
}
}

private makeIsuniqueRule(rule: IsuniqueRule, locationdescription: string, severity: string, message: string): string {
const listpropertyName:string = rule.listproperty.appliedfeature.toFreString();
const listName: string = rule.list.appliedfeature.toFreString();
const uniquelistName: string = `unique${Names.startWithUpperCase(listpropertyName)}In${Names.startWithUpperCase(listName)}`;
const referredListproperty: FreMetaProperty | undefined = rule.listproperty.findRefOfLastAppliedFeature();
let listpropertyTypeName: string = '';
let listpropertyTypescript: string = '';
if (!!referredListproperty) {
listpropertyTypeName = GenerationUtil.getBaseTypeAsString(referredListproperty);
listpropertyTypescript = GenerationUtil.langExpToTypeScript(rule.listproperty.appliedfeature);
}
//
let refAddition: string = "";
let howToWriteName: string = "this.myWriter.writeNameOnly(elem)";
if (!!rule.list.findRefOfLastAppliedFeature() && !rule.list.findRefOfLastAppliedFeature()!.isPart) { // the elements in the list are all FreElementReferences
refAddition += ".referred";
howToWriteName = "elem.name"; // if the list element is a reference there is no need to call the writer
}
//
if (message.length === 0) {
message = `\`The value of property '${listpropertyName}' (\"\${${howToWriteName}}\") is not unique in list '${listName}'\``;
}
return `let ${uniquelistName}: ${listpropertyTypeName}[] = [];
private makeIsuniqueRule(rule: IsUniqueRule, locationdescription: string, severity: string, message?: string): string {
if (!!rule.listproperty && !!rule.list) {
const listpropertyName: string = rule.listproperty.appliedfeature.toFreString();
const listName: string = rule.list.appliedfeature.toFreString();
const uniquelistName: string = `unique${Names.startWithUpperCase(listpropertyName)}In${Names.startWithUpperCase(listName)}`;
const referredListproperty: FreMetaProperty | undefined = rule.listproperty.findRefOfLastAppliedFeature();
let listpropertyTypeName: string = '';
let listpropertyTypescript: string = '';
if (!!referredListproperty) {
listpropertyTypeName = GenerationUtil.getBaseTypeAsString(referredListproperty);
listpropertyTypescript = GenerationUtil.langExpToTypeScript(rule.listproperty.appliedfeature);
}
//
let refAddition: string = "";
let howToWriteName: string = "this.myWriter.writeNameOnly(elem)";
if (!!rule.list.findRefOfLastAppliedFeature() && !rule.list.findRefOfLastAppliedFeature()!.isPart) { // the elements in the list are all FreElementReferences
refAddition += ".referred";
howToWriteName = "elem.name"; // if the list element is a reference there is no need to call the writer
}
//
if (!message || message.length === 0) {
message = `\`The value of property '${listpropertyName}' (\"\${${howToWriteName}}\") is not unique in list '${listName}'\``;
}
return `let ${uniquelistName}: ${listpropertyTypeName}[] = [];
${GenerationUtil.langExpToTypeScript(rule.list)}.forEach((elem, index) => {
if ((elem === undefined) || (elem === null)) {
this.errorList.push(new ${Names.FreError}(\`Element[\$\{index\}] of property '${listName}' has no value\`,
Expand All @@ -267,17 +289,20 @@ export class RulesCheckerTemplate {
${rule.severity.severity === FreErrorSeverity.Error ? `hasFatalError = true;` : ``}
}
});`;
} else {
return "<error in makeIsuniqueRule>"
}
}

private makeMessage(message: ValidationMessage): string {
let result = "";
let result: string = "";
if (!!message) {
const numberOfparts = message.content.length;
message.content.forEach((cont, index) => {
if (cont instanceof ValidationMessageText) {
// console.log("FOUND message text: '" + cont.value + "'");
result += `${cont.value}`;
} else if (cont instanceof ValidationMessageReference) {
} else if (cont instanceof ValidationMessageReference && !!cont.expression) {
if (cont.expression.findRefOfLastAppliedFeature() instanceof FreMetaPrimitiveProperty) {
result += `\${${GenerationUtil.langExpToTypeScript(cont.expression)}}`;
} else {
Expand Down
Loading

0 comments on commit 7ecb908

Please sign in to comment.