+21
@@ -0,0 +1,21 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 TypeFox GmbH
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import type { ValidationAcceptor } from '../../validation/validation-registry.js';
|
||||
import type { LangiumGrammarServices } from '../langium-grammar-module.js';
|
||||
import * as ast from '../../languages/generated/ast.js';
|
||||
export declare function registerTypeValidationChecks(services: LangiumGrammarServices): void;
|
||||
export declare class LangiumGrammarTypesValidator {
|
||||
checkAttributeDefaultValue(grammarInterface: ast.Interface, accept: ValidationAcceptor): void;
|
||||
checkCyclicType(type: ast.Type, accept: ValidationAcceptor): void;
|
||||
checkCyclicInterface(type: ast.Interface, accept: ValidationAcceptor): void;
|
||||
checkDeclaredTypesConsistency(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkDeclaredAndInferredTypesConsistency(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkActionIsNotUnionType(action: ast.Action, accept: ValidationAcceptor): void;
|
||||
checkInfixRuleExplicitReturnType(infixRule: ast.InfixRule, accept: ValidationAcceptor): void;
|
||||
private isValidOperatorPropertyType;
|
||||
private isValidLeftRightPropertyType;
|
||||
}
|
||||
//# sourceMappingURL=types-validator.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types-validator.d.ts","sourceRoot":"","sources":["../../../src/grammar/validation/types-validator.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,KAAK,EAAkB,kBAAkB,EAAoB,MAAM,yCAAyC,CAAC;AACpH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAG3E,OAAO,KAAK,GAAG,MAAM,kCAAkC,CAAC;AAQxD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAuBnF;AAED,qBAAa,4BAA4B;IAErC,0BAA0B,CAAC,gBAAgB,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAiB7F,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAMjE,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAM3E,6BAA6B,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAarF,wCAAwC,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAchG,yBAAyB,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAM/E,gCAAgC,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAgE5F,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;CAQvC"}
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 TypeFox GmbH
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import * as ast from '../../languages/generated/ast.js';
|
||||
import { MultiMap } from '../../utils/collections.js';
|
||||
import { extractAssignments } from '../internal-grammar-util.js';
|
||||
import { getExplicitRuleType } from '../../utils/grammar-utils.js';
|
||||
import { flattenPropertyUnion, InterfaceType, isArrayType, isInterfaceType, isMandatoryPropertyType, isPrimitiveType, isPropertyUnion, isReferenceType, isStringType, isTypeAssignable, isUnionType, isValueType, propertyTypeToString } from '../type-system/type-collector/types.js';
|
||||
import { getTypeOption, isDeclared, isInferred, isInferredAndDeclared } from '../workspace/documents.js';
|
||||
import { getDocument } from '../../utils/ast-utils.js';
|
||||
export function registerTypeValidationChecks(services) {
|
||||
const registry = services.validation.ValidationRegistry;
|
||||
const typesValidator = services.validation.LangiumGrammarTypesValidator;
|
||||
const checks = {
|
||||
Action: [
|
||||
typesValidator.checkActionIsNotUnionType,
|
||||
],
|
||||
Grammar: [
|
||||
typesValidator.checkDeclaredTypesConsistency,
|
||||
typesValidator.checkDeclaredAndInferredTypesConsistency
|
||||
],
|
||||
InfixRule: [
|
||||
typesValidator.checkInfixRuleExplicitReturnType,
|
||||
],
|
||||
Interface: [
|
||||
typesValidator.checkAttributeDefaultValue,
|
||||
typesValidator.checkCyclicInterface
|
||||
],
|
||||
Type: [
|
||||
typesValidator.checkCyclicType
|
||||
]
|
||||
};
|
||||
registry.register(checks, typesValidator);
|
||||
}
|
||||
export class LangiumGrammarTypesValidator {
|
||||
checkAttributeDefaultValue(grammarInterface, accept) {
|
||||
const validationResources = getDocument(grammarInterface)?.validationResources;
|
||||
if (!validationResources) {
|
||||
return;
|
||||
}
|
||||
const matchedProperties = matchInterfaceAttributes(validationResources, grammarInterface);
|
||||
for (const [grammarProperty, property] of matchedProperties) {
|
||||
const defaultType = getDefaultValueType(grammarProperty.defaultValue);
|
||||
if (defaultType && !isTypeAssignable(defaultType, property.type)) {
|
||||
accept('error', `Cannot assign default value of type '${propertyTypeToString(defaultType, 'DeclaredType')}' to type '${propertyTypeToString(property.type, 'DeclaredType')}'.`, {
|
||||
node: grammarProperty,
|
||||
property: 'defaultValue'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
checkCyclicType(type, accept) {
|
||||
if (isCyclicType(type, new Set())) {
|
||||
accept('error', `Type alias '${type.name}' circularly references itself.`, { node: type, property: 'name' });
|
||||
}
|
||||
}
|
||||
checkCyclicInterface(type, accept) {
|
||||
if (isCyclicType(type, new Set())) {
|
||||
accept('error', `Type '${type.name}' recursively references itself as a base type.`, { node: type, property: 'name' });
|
||||
}
|
||||
}
|
||||
checkDeclaredTypesConsistency(grammar, accept) {
|
||||
const validationResources = grammar.$document?.validationResources;
|
||||
if (validationResources) {
|
||||
for (const typeInfo of validationResources.typeToValidationInfo.values()) {
|
||||
if (isDeclared(typeInfo) && isInterfaceType(typeInfo.declared) && ast.isInterface(typeInfo.declaredNode)) {
|
||||
const declInterface = typeInfo;
|
||||
validateInterfaceSuperTypes(declInterface, accept);
|
||||
validateSuperTypesConsistency(declInterface, accept);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkDeclaredAndInferredTypesConsistency(grammar, accept) {
|
||||
const validationResources = grammar.$document?.validationResources;
|
||||
if (validationResources) {
|
||||
for (const typeInfo of validationResources.typeToValidationInfo.values()) {
|
||||
if (isInferred(typeInfo) && typeInfo.inferred instanceof InterfaceType) {
|
||||
validateInferredInterface(typeInfo.inferred, accept);
|
||||
}
|
||||
if (isInferredAndDeclared(typeInfo)) {
|
||||
validateDeclaredAndInferredConsistency(typeInfo, validationResources, accept);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkActionIsNotUnionType(action, accept) {
|
||||
if (ast.isType(action.type)) {
|
||||
accept('error', 'Actions cannot create union types.', { node: action, property: 'type' });
|
||||
}
|
||||
}
|
||||
checkInfixRuleExplicitReturnType(infixRule, accept) {
|
||||
if (!infixRule.returnType?.ref) {
|
||||
return;
|
||||
}
|
||||
const validationResources = getDocument(infixRule)?.validationResources;
|
||||
if (!validationResources) {
|
||||
return;
|
||||
}
|
||||
const returnTypeName = infixRule.returnType.ref.name;
|
||||
const returnTypeProperties = validationResources.typeToSuperProperties.get(returnTypeName);
|
||||
if (!returnTypeProperties) {
|
||||
return;
|
||||
}
|
||||
const propertyMap = new Map(returnTypeProperties.map(prop => [prop.name, prop]));
|
||||
// Check for 'operator' property
|
||||
const operatorProp = propertyMap.get('operator');
|
||||
if (!operatorProp) {
|
||||
accept('error', `Infix rule '${infixRule.name}' with explicit return type '${returnTypeName}' must have an 'operator' property.`, {
|
||||
node: infixRule,
|
||||
property: 'returnType'
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Validate operator property type
|
||||
const operatorKeywords = infixRule.operators.precedences.flatMap(prec => prec.operators.map(op => op.value));
|
||||
const isValidOperatorType = this.isValidOperatorPropertyType(operatorProp.type, operatorKeywords);
|
||||
if (!isValidOperatorType) {
|
||||
const expectedType = operatorKeywords.length > 1
|
||||
? `one of: ${operatorKeywords.map(op => `'${op}'`).join(', ')}`
|
||||
: `'${operatorKeywords[0]}'`;
|
||||
accept('error', `Property 'operator' must be of type 'string' or ${expectedType}.`, {
|
||||
node: infixRule,
|
||||
property: 'returnType'
|
||||
});
|
||||
}
|
||||
}
|
||||
// Check for 'left' and 'right' properties
|
||||
const callRule = infixRule.call.rule.ref;
|
||||
if (!callRule) {
|
||||
return;
|
||||
}
|
||||
const callRuleType = getExplicitRuleType(callRule) ?? callRule.name;
|
||||
for (const propName of ['left', 'right']) {
|
||||
const prop = propertyMap.get(propName);
|
||||
if (!prop) {
|
||||
accept('error', `Infix rule '${infixRule.name}' with explicit return type '${returnTypeName}' must have a '${propName}' property.`, {
|
||||
node: infixRule,
|
||||
property: 'returnType'
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Validate that the property type matches or is a supertype of the called rule
|
||||
const isValidType = this.isValidLeftRightPropertyType(prop.type, callRuleType, validationResources);
|
||||
if (!isValidType) {
|
||||
accept('error', `Property '${propName}' must be of type '${callRuleType}' or a supertype of it.`, {
|
||||
node: infixRule,
|
||||
property: 'returnType'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
isValidOperatorPropertyType(propertyType, operatorKeywords) {
|
||||
const union = {
|
||||
types: operatorKeywords.map(keyword => ({ string: keyword }))
|
||||
};
|
||||
return isTypeAssignable(union, propertyType);
|
||||
}
|
||||
isValidLeftRightPropertyType(propertyType, callRuleTypeName, validationResources) {
|
||||
const callRuleTypeInfo = validationResources.typeToValidationInfo.get(callRuleTypeName);
|
||||
if (callRuleTypeInfo) {
|
||||
const callRuleType = getTypeOption(callRuleTypeInfo);
|
||||
return isTypeAssignable({ value: callRuleType }, propertyType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function matchInterfaceAttributes(resources, grammarInterface) {
|
||||
const elements = [];
|
||||
const interfaceType = resources.typeToValidationInfo.get(grammarInterface.name);
|
||||
if (interfaceType && isDeclared(interfaceType) && isInterfaceType(interfaceType.declared)) {
|
||||
for (const grammarProperty of grammarInterface.attributes.filter(prop => prop.defaultValue)) {
|
||||
const property = interfaceType.declared.properties.find(e => e.name === grammarProperty.name);
|
||||
if (property) {
|
||||
elements.push([grammarProperty, property]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
function getDefaultValueType(defaultValue) {
|
||||
if (ast.isBooleanLiteral(defaultValue)) {
|
||||
return { primitive: 'boolean' };
|
||||
}
|
||||
else if (ast.isNumberLiteral(defaultValue)) {
|
||||
return { primitive: 'number' };
|
||||
}
|
||||
else if (ast.isStringLiteral(defaultValue)) {
|
||||
return { string: defaultValue.value };
|
||||
}
|
||||
else if (ast.isArrayLiteral(defaultValue)) {
|
||||
return { elementType: generateElementType(defaultValue) };
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
function generateElementType(arrayLiteral) {
|
||||
if (arrayLiteral.elements.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const foundTypes = [];
|
||||
for (const element of arrayLiteral.elements) {
|
||||
const elementType = getDefaultValueType(element);
|
||||
if (!elementType) {
|
||||
continue;
|
||||
}
|
||||
if (isPrimitiveType(elementType)) {
|
||||
if (!(foundTypes.some(e => isPrimitiveType(e) && e.primitive === elementType.primitive))) {
|
||||
foundTypes.push(elementType);
|
||||
}
|
||||
}
|
||||
else if (isStringType(elementType)) {
|
||||
if (!(foundTypes.some(e => isStringType(e) && e.string === elementType.string))) {
|
||||
foundTypes.push(elementType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foundTypes.push(elementType);
|
||||
}
|
||||
}
|
||||
if (foundTypes.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
else if (foundTypes.length === 1) {
|
||||
return foundTypes[0];
|
||||
}
|
||||
else {
|
||||
return {
|
||||
types: foundTypes
|
||||
};
|
||||
}
|
||||
}
|
||||
function isCyclicType(type, visited) {
|
||||
if (visited.has(type)) {
|
||||
return true;
|
||||
}
|
||||
visited.add(type);
|
||||
if (ast.isType(type)) {
|
||||
return isCyclicType(type.type, visited);
|
||||
}
|
||||
else if (ast.isInterface(type)) {
|
||||
return type.superTypes.some(t => t.ref && isCyclicType(t.ref, new Set(visited)));
|
||||
}
|
||||
else if (ast.isSimpleType(type)) {
|
||||
if (type.typeRef?.ref) {
|
||||
return isCyclicType(type.typeRef.ref, visited);
|
||||
}
|
||||
}
|
||||
else if (ast.isReferenceType(type)) {
|
||||
return isCyclicType(type.referenceType, visited);
|
||||
}
|
||||
else if (ast.isArrayType(type)) {
|
||||
return isCyclicType(type.elementType, visited);
|
||||
}
|
||||
else if (ast.isUnionType(type)) {
|
||||
return type.types.some(t => isCyclicType(t, new Set(visited)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function validateInferredInterface(inferredInterface, accept) {
|
||||
inferredInterface.properties.forEach(prop => {
|
||||
const flattened = flattenPropertyUnion(prop.type);
|
||||
if (flattened.length > 1) {
|
||||
const typeKind = (type) => isReferenceType(type) ? (type.isMulti ? 'multi-ref' : 'ref') : 'other';
|
||||
const firstKind = typeKind(flattened[0]);
|
||||
if (flattened.slice(1).some(type => typeKind(type) !== firstKind)) {
|
||||
const targetNode = prop.astNodes.values().next()?.value;
|
||||
if (targetNode) {
|
||||
accept('error', `Mixing a cross-reference with other types is not supported. Consider splitting property "${prop.name}" into two or more different properties.`, { node: targetNode });
|
||||
}
|
||||
}
|
||||
}
|
||||
const referenceTypes = collectReferenceTypes(prop.type);
|
||||
for (const refType of referenceTypes) {
|
||||
if (refType.isMulti && refType.isSingle) {
|
||||
const targetNode = prop.astNodes.values().next()?.value;
|
||||
if (targetNode) {
|
||||
accept('error', `Multi references and normal references cannot be mixed. Consider splitting property "${prop.name}" into two or more different properties.`, { node: targetNode });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function collectReferenceTypes(type) {
|
||||
const result = new Set();
|
||||
if (isReferenceType(type)) {
|
||||
result.add(type);
|
||||
}
|
||||
else if (isArrayType(type) && type.elementType) {
|
||||
const elementTypes = collectReferenceTypes(type.elementType);
|
||||
elementTypes.forEach(e => result.add(e));
|
||||
}
|
||||
else if (isPropertyUnion(type)) {
|
||||
type.types.forEach(t => {
|
||||
const subTypes = collectReferenceTypes(t);
|
||||
subTypes.forEach(e => result.add(e));
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function validateInterfaceSuperTypes({ declared, declaredNode }, accept) {
|
||||
Array.from(declared.superTypes).forEach((superType, i) => {
|
||||
if (superType) {
|
||||
if (isUnionType(superType)) {
|
||||
accept('error', 'Interfaces cannot extend union types.', { node: declaredNode, property: 'superTypes', index: i });
|
||||
}
|
||||
if (!superType.declared) {
|
||||
accept('error', 'Extending an inferred type is discouraged.', { node: declaredNode, property: 'superTypes', index: i });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function validateSuperTypesConsistency({ declared, declaredNode }, accept) {
|
||||
const nameToProp = declared.properties.reduce((acc, e) => acc.add(e.name, e), new MultiMap());
|
||||
for (const [name, props] of nameToProp.entriesGroupedByKey()) {
|
||||
if (props.length > 1) {
|
||||
for (const prop of props) {
|
||||
accept('error', `Cannot have two properties with the same name '${name}'.`, {
|
||||
node: Array.from(prop.astNodes)[0],
|
||||
property: 'name'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const allSuperTypes = Array.from(declared.superTypes);
|
||||
for (let i = 0; i < allSuperTypes.length; i++) {
|
||||
for (let j = i + 1; j < allSuperTypes.length; j++) {
|
||||
const outerType = allSuperTypes[i];
|
||||
const innerType = allSuperTypes[j];
|
||||
const outerProps = isInterfaceType(outerType) ? outerType.superProperties : [];
|
||||
const innerProps = isInterfaceType(innerType) ? innerType.superProperties : [];
|
||||
const nonIdentical = getNonIdenticalProps(outerProps, innerProps);
|
||||
if (nonIdentical.length > 0) {
|
||||
accept('error', `Cannot simultaneously inherit from '${outerType}' and '${innerType}'. Their ${nonIdentical.map(e => "'" + e + "'").join(', ')} properties are not identical.`, {
|
||||
node: declaredNode,
|
||||
property: 'name'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const allSuperProps = new Set();
|
||||
for (const superType of allSuperTypes) {
|
||||
const props = isInterfaceType(superType) ? superType.superProperties : [];
|
||||
for (const prop of props) {
|
||||
allSuperProps.add(prop.name);
|
||||
}
|
||||
}
|
||||
for (const ownProp of declared.properties) {
|
||||
if (allSuperProps.has(ownProp.name)) {
|
||||
const propNode = declaredNode.attributes.find(e => e.name === ownProp.name);
|
||||
if (propNode) {
|
||||
accept('error', `Cannot redeclare property '${ownProp.name}'. It is already inherited from another interface.`, {
|
||||
node: propNode,
|
||||
property: 'name'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function getNonIdenticalProps(a, b) {
|
||||
const nonIdentical = [];
|
||||
for (const outerProp of a) {
|
||||
const innerProp = b.find(e => e.name === outerProp.name);
|
||||
if (innerProp && !arePropTypesIdentical(outerProp, innerProp)) {
|
||||
nonIdentical.push(outerProp.name);
|
||||
}
|
||||
}
|
||||
return nonIdentical;
|
||||
}
|
||||
function arePropTypesIdentical(a, b) {
|
||||
return isTypeAssignable(a.type, b.type) && isTypeAssignable(b.type, a.type);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
function validateDeclaredAndInferredConsistency(typeInfo, resources, accept) {
|
||||
const { inferred, declared, declaredNode, inferredNodes } = typeInfo;
|
||||
const typeName = declared.name;
|
||||
const applyErrorToRulesAndActions = (msgPostfix) => (errorMsg) => inferredNodes.forEach(node => accept('error', `${errorMsg}${msgPostfix ? ` ${msgPostfix}` : ''}.`, (node?.inferredType) ?
|
||||
{ node: node?.inferredType, property: 'name' } :
|
||||
{ node, property: ast.isAction(node) ? 'type' : 'name' }));
|
||||
const applyErrorToProperties = (nodes, errorMessage) => nodes.forEach(node => accept('error', errorMessage, { node, property: ast.isAssignment(node) || ast.isAction(node) ? 'feature' : 'name' }));
|
||||
// todo add actions
|
||||
// currently we don't track which assignments belong to which actions and can't apply this error
|
||||
const applyMissingPropErrorToRules = (missingProp) => {
|
||||
inferredNodes.forEach(node => {
|
||||
if (ast.isParserRule(node)) {
|
||||
const assignments = extractAssignments(node.definition);
|
||||
if (assignments.find(e => e.feature === missingProp) === undefined) {
|
||||
accept('error', `Property '${missingProp}' is missing in a rule '${node.name}', but is required in type '${typeName}'.`, {
|
||||
node,
|
||||
property: 'parameters'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (isUnionType(inferred) && isUnionType(declared)) {
|
||||
validateAlternativesConsistency(inferred.type, declared.type, applyErrorToRulesAndActions(`in a rule that returns type '${typeName}'`));
|
||||
}
|
||||
else if (isInterfaceType(inferred) && isInterfaceType(declared)) {
|
||||
validatePropertiesConsistency(inferred, declared, resources, applyErrorToRulesAndActions(`in a rule that returns type '${typeName}'`), applyErrorToProperties, applyMissingPropErrorToRules);
|
||||
}
|
||||
else {
|
||||
const errorMessage = `Inferred and declared versions of type '${typeName}' both have to be interfaces or unions.`;
|
||||
applyErrorToRulesAndActions()(errorMessage);
|
||||
accept('error', errorMessage, { node: declaredNode, property: 'name' });
|
||||
}
|
||||
}
|
||||
function validateAlternativesConsistency(inferred, declared, applyErrorToInferredTypes) {
|
||||
if (!isTypeAssignable(inferred, declared)) {
|
||||
applyErrorToInferredTypes(`Cannot assign type '${propertyTypeToString(inferred, 'DeclaredType')}' to '${propertyTypeToString(declared, 'DeclaredType')}'`);
|
||||
}
|
||||
}
|
||||
function isOptionalProperty(prop) {
|
||||
// mandatory properties will always be created so there are no issues if they are missing
|
||||
return prop.optional || isMandatoryPropertyType(prop.type);
|
||||
}
|
||||
function validatePropertiesConsistency(inferred, declared, resources, applyErrorToType, applyErrorToProperties, applyMissingPropErrorToRules) {
|
||||
const ownInferredProps = new Set(inferred.properties.map(e => e.name));
|
||||
// This field also contains properties of sub types
|
||||
const allInferredProps = new Map(inferred.allProperties.map(e => [e.name, e]));
|
||||
// This field only contains properties of itself or super types
|
||||
const declaredProps = new Map(declared.superProperties.map(e => [e.name, e]));
|
||||
// The inferred props may not have full hierarchy information so try finding
|
||||
// a corresponding declared type
|
||||
const matchingProp = (type) => {
|
||||
if (isPropertyUnion(type))
|
||||
return {
|
||||
types: type.types.map(t => matchingProp(t))
|
||||
};
|
||||
if (isReferenceType(type))
|
||||
return {
|
||||
referenceType: matchingProp(type.referenceType),
|
||||
isMulti: type.isMulti,
|
||||
isSingle: type.isSingle
|
||||
};
|
||||
if (isArrayType(type))
|
||||
return {
|
||||
elementType: type.elementType && matchingProp(type.elementType)
|
||||
};
|
||||
if (isValueType(type)) {
|
||||
const resource = resources.typeToValidationInfo.get(type.value.name);
|
||||
if (!resource)
|
||||
return type;
|
||||
return { value: 'declared' in resource ? resource.declared : resource.inferred };
|
||||
}
|
||||
return type;
|
||||
};
|
||||
// detects extra properties & validates matched ones on consistency by the 'optional' property
|
||||
for (const [name, foundProp] of allInferredProps.entries()) {
|
||||
const expectedProp = declaredProps.get(name);
|
||||
if (expectedProp) {
|
||||
const foundTypeAsStr = propertyTypeToString(foundProp.type, 'DeclaredType');
|
||||
const expectedTypeAsStr = propertyTypeToString(expectedProp.type, 'DeclaredType');
|
||||
const typeAlternativesErrors = isTypeAssignable(matchingProp(foundProp.type), expectedProp.type);
|
||||
if (!typeAlternativesErrors && expectedTypeAsStr !== 'unknown') {
|
||||
const errorMsgPrefix = `The assigned type '${foundTypeAsStr}' is not compatible with the declared property '${name}' of type '${expectedTypeAsStr}'.`;
|
||||
applyErrorToProperties(foundProp.astNodes, errorMsgPrefix);
|
||||
}
|
||||
if (foundProp.optional && !isOptionalProperty(expectedProp)) {
|
||||
applyMissingPropErrorToRules(name);
|
||||
}
|
||||
}
|
||||
else if (ownInferredProps.has(name)) {
|
||||
// Only apply the superfluous property error on properties which are actually declared on the current type
|
||||
applyErrorToProperties(foundProp.astNodes, `A property '${name}' is not expected.`);
|
||||
}
|
||||
}
|
||||
// Detect any missing properties
|
||||
const missingProps = new Set();
|
||||
for (const [name, expectedProperties] of declaredProps.entries()) {
|
||||
const foundProperty = allInferredProps.get(name);
|
||||
if (!foundProperty && !isOptionalProperty(expectedProperties) && expectedProperties.defaultValue === undefined) {
|
||||
missingProps.add(name);
|
||||
}
|
||||
}
|
||||
if (missingProps.size > 0) {
|
||||
const prefix = missingProps.size > 1 ? 'Properties' : 'A property';
|
||||
const postfix = missingProps.size > 1 ? 'are expected' : 'is expected';
|
||||
const props = Array.from(missingProps).map(e => `'${e}'`).sort().join(', ');
|
||||
applyErrorToType(`${prefix} ${props} ${postfix}.`);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=types-validator.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 TypeFox GmbH
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import type { Grammar } from '../../languages/generated/ast.js';
|
||||
import type { ValidationResources } from '../workspace/documents.js';
|
||||
import type { LangiumGrammarServices } from '../langium-grammar-module.js';
|
||||
export declare class LangiumGrammarValidationResourcesCollector {
|
||||
private readonly services;
|
||||
constructor(services: LangiumGrammarServices);
|
||||
collectValidationResources(grammar: Grammar): ValidationResources;
|
||||
private collectValidationInfo;
|
||||
private collectSuperProperties;
|
||||
private addSuperProperties;
|
||||
}
|
||||
//# sourceMappingURL=validation-resources-collector.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"validation-resources-collector.d.ts","sourceRoot":"","sources":["../../../src/grammar/validation/validation-resources-collector.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAGhF,OAAO,KAAK,EAA2B,OAAO,EAA+B,MAAM,kCAAkC,CAAC;AAEtH,OAAO,KAAK,EAAwB,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAS3E,qBAAa,0CAA0C;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;gBAEnC,QAAQ,EAAE,sBAAsB;IAI5C,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB;IAajE,OAAO,CAAC,qBAAqB;IA8B7B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,kBAAkB;CAc7B"}
|
||||
Generated
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 TypeFox GmbH
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import { MultiMap } from '../../utils/collections.js';
|
||||
import { stream } from '../../utils/stream.js';
|
||||
import { isAction, isAlternatives, isGroup, isUnorderedGroup } from '../../languages/generated/ast.js';
|
||||
import { mergeInterfaces, mergeTypesAndInterfaces } from '../type-system/types-util.js';
|
||||
import { collectValidationAst } from '../type-system/ast-collector.js';
|
||||
import { getActionType, getRuleTypeName } from '../../utils/grammar-utils.js';
|
||||
export class LangiumGrammarValidationResourcesCollector {
|
||||
constructor(services) {
|
||||
this.services = services;
|
||||
}
|
||||
collectValidationResources(grammar) {
|
||||
try {
|
||||
const typeResources = collectValidationAst(grammar, this.services);
|
||||
return {
|
||||
typeToValidationInfo: this.collectValidationInfo(typeResources),
|
||||
typeToSuperProperties: this.collectSuperProperties(typeResources),
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Error collecting validation resources', err);
|
||||
return { typeToValidationInfo: new Map(), typeToSuperProperties: new Map() };
|
||||
}
|
||||
}
|
||||
collectValidationInfo({ astResources, inferred, declared }) {
|
||||
const res = new Map();
|
||||
const typeNameToRulesActions = collectNameToRulesActions(astResources);
|
||||
for (const type of mergeTypesAndInterfaces(inferred)) {
|
||||
res.set(type.name, { inferred: type, inferredNodes: typeNameToRulesActions.get(type.name) });
|
||||
}
|
||||
const typeNametoInterfacesUnions = stream(astResources.interfaces)
|
||||
.concat(astResources.types)
|
||||
.reduce((acc, type) => acc.set(type.name, type), new Map());
|
||||
for (const type of mergeTypesAndInterfaces(declared)) {
|
||||
const node = typeNametoInterfacesUnions.get(type.name);
|
||||
if (node) {
|
||||
const inferred = res.get(type.name);
|
||||
res.set(type.name, { ...inferred ?? {}, declared: type, declaredNode: node });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
collectSuperProperties({ inferred, declared }) {
|
||||
const typeToSuperProperties = new Map();
|
||||
const interfaces = mergeInterfaces(inferred, declared);
|
||||
const interfaceMap = new Map(interfaces.map(e => [e.name, e]));
|
||||
for (const type of mergeInterfaces(inferred, declared)) {
|
||||
typeToSuperProperties.set(type.name, this.addSuperProperties(type, interfaceMap, new Set()));
|
||||
}
|
||||
return typeToSuperProperties;
|
||||
}
|
||||
addSuperProperties(interfaceType, map, visited) {
|
||||
if (visited.has(interfaceType.name)) {
|
||||
return [];
|
||||
}
|
||||
visited.add(interfaceType.name);
|
||||
const properties = [...interfaceType.properties];
|
||||
for (const superType of interfaceType.superTypes) {
|
||||
const value = map.get(superType.name);
|
||||
if (value) {
|
||||
properties.push(...this.addSuperProperties(value, map, visited));
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
function collectNameToRulesActions({ parserRules, datatypeRules }) {
|
||||
const acc = new MultiMap();
|
||||
// collect rules
|
||||
stream(parserRules)
|
||||
.concat(datatypeRules)
|
||||
.forEach(rule => acc.add(getRuleTypeName(rule), rule));
|
||||
// collect actions
|
||||
function collectActions(element) {
|
||||
if (isAction(element)) {
|
||||
const name = getActionType(element);
|
||||
if (name) {
|
||||
acc.add(name, element);
|
||||
}
|
||||
}
|
||||
if (isAlternatives(element) || isGroup(element) || isUnorderedGroup(element)) {
|
||||
element.elements.forEach(e => collectActions(e));
|
||||
}
|
||||
}
|
||||
parserRules
|
||||
.forEach(rule => collectActions(rule.definition));
|
||||
return acc;
|
||||
}
|
||||
//# sourceMappingURL=validation-resources-collector.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"validation-resources-collector.js","sourceRoot":"","sources":["../../../src/grammar/validation/validation-resources-collector.ts"],"names":[],"mappings":"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"}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2021 TypeFox GmbH
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import * as ast from '../../languages/generated/ast.js';
|
||||
import type { References } from '../../references/references.js';
|
||||
import type { Reference } from '../../syntax-tree.js';
|
||||
import type { ValidationAcceptor } from '../../validation/validation-registry.js';
|
||||
import type { AstNodeLocator } from '../../workspace/ast-node-locator.js';
|
||||
import type { LangiumDocuments } from '../../workspace/documents.js';
|
||||
import type { LangiumGrammarServices } from '../langium-grammar-module.js';
|
||||
export interface LangiumGrammarValidationOptions {
|
||||
/**
|
||||
* Handling of type definitions and inference in the grammar.
|
||||
*
|
||||
* `normal` allows both inferred and declared types, `strict` only allows declared types.
|
||||
*/
|
||||
types?: 'normal' | 'strict';
|
||||
}
|
||||
export declare function registerValidationChecks(services: LangiumGrammarServices): void;
|
||||
export declare namespace IssueCodes {
|
||||
const GrammarNameUppercase = "grammar-name-uppercase";
|
||||
const RuleNameUppercase = "rule-name-uppercase";
|
||||
const UseRegexTokens = "use-regex-tokens";
|
||||
const EntryRuleTokenSyntax = "entry-rule-token-syntax";
|
||||
const CrossRefTokenSyntax = "cross-ref-token-syntax";
|
||||
const ParserRuleToTypeDecl = "parser-rule-to-type-decl";
|
||||
const UnnecessaryFileExtension = "unnecessary-file-extension";
|
||||
const InvalidReturns = "invalid-returns";
|
||||
const InvalidInfers = "invalid-infers";
|
||||
const MissingInfer = "missing-infer";
|
||||
const MissingReturns = "missing-returns";
|
||||
const MissingCrossRefTerminal = "missing-cross-ref-terminal";
|
||||
const SuperfluousInfer = "superfluous-infer";
|
||||
const OptionalUnorderedGroup = "optional-unordered-group";
|
||||
const ParsingRuleEmpty = "parsing-rule-empty";
|
||||
}
|
||||
export declare class LangiumGrammarValidator {
|
||||
options: LangiumGrammarValidationOptions;
|
||||
protected readonly references: References;
|
||||
protected readonly nodeLocator: AstNodeLocator;
|
||||
protected readonly documents: LangiumDocuments;
|
||||
constructor(services: LangiumGrammarServices);
|
||||
checkGrammarName(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkEntryGrammarRule(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
/**
|
||||
* Check whether any rule defined in this grammar is a duplicate of an already defined rule or an imported rule
|
||||
*/
|
||||
checkUniqueRuleName(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
/**
|
||||
* Check whether any type defined in this grammar is a duplicate of an already defined type or an imported type
|
||||
*/
|
||||
checkUniqueTypeName(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
private checkUniqueName;
|
||||
checkUniqueTypeAndGrammarNames(inputGrammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkDuplicateImportedGrammar(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
/**
|
||||
* Compared to the validation above, this validation only checks whether two imported grammars export the same grammar rule.
|
||||
*/
|
||||
checkUniqueImportedRules(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
private getDuplicateExportedRules;
|
||||
checkGrammarTypeInfer(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
private getActionType;
|
||||
checkHiddenTerminalRule(terminalRule: ast.TerminalRule, accept: ValidationAcceptor): void;
|
||||
checkEmptyTerminalRule(terminalRule: ast.TerminalRule, accept: ValidationAcceptor): void;
|
||||
checkEmptyParserRule(parserRule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
checkInvalidRegexFlags(token: ast.RegexToken, accept: ValidationAcceptor): void;
|
||||
checkDirectlyUsedRegexFlags(token: ast.RegexToken, accept: ValidationAcceptor): void;
|
||||
private getFlagRange;
|
||||
checkUsedHiddenTerminalRule(ruleCall: ast.RuleCall | ast.TerminalRuleCall, accept: ValidationAcceptor): void;
|
||||
checkUsedFragmentTerminalRule(ruleCall: ast.RuleCall, accept: ValidationAcceptor): void;
|
||||
checkCrossReferenceSyntax(crossRef: ast.CrossReference, accept: ValidationAcceptor): void;
|
||||
checkPackageImport(imp: ast.GrammarImport, accept: ValidationAcceptor): void;
|
||||
checkInvalidCharacterRange(range: ast.CharacterRange, accept: ValidationAcceptor): void;
|
||||
checkGrammarForUnusedRules(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkClashingTerminalNames(grammar: ast.Grammar, accept: ValidationAcceptor): void;
|
||||
checkRuleName(rule: ast.AbstractRule, accept: ValidationAcceptor): void;
|
||||
/** This validation checks, that parser rules which are called multiple times are assigned (except for fragments). */
|
||||
checkMultiRuleCallsAreAssigned(call: ast.RuleCall, accept: ValidationAcceptor): void;
|
||||
checkTypeReservedName(type: ast.Interface | ast.TypeAttribute | ast.Type | ast.InferredType, accept: ValidationAcceptor): void;
|
||||
checkAssignmentReservedName(assignment: ast.Assignment | ast.Action, accept: ValidationAcceptor): void;
|
||||
checkParserRuleReservedName(rule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
private checkReservedName;
|
||||
checkKeyword(keyword: ast.Keyword, accept: ValidationAcceptor): void;
|
||||
checkUnorderedGroup(unorderedGroup: ast.UnorderedGroup, accept: ValidationAcceptor): void;
|
||||
checkRuleParameters(rule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
checkParserRuleDataType(rule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
checkFragmentKeywords(rule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
checkInfixRuleDataType(rule: ast.InfixRule, accept: ValidationAcceptor): void;
|
||||
checkAssignmentToFragmentRule(assignment: ast.Assignment, accept: ValidationAcceptor): void;
|
||||
checkAssignmentTypes(assignment: ast.Assignment, accept: ValidationAcceptor): void;
|
||||
/** This validation recursively looks at all assignments (and rewriting actions) with '=' as assignment operator and checks,
|
||||
* whether the operator should be '+=' instead. */
|
||||
checkOperatorMultiplicitiesForMultiAssignments(rule: ast.ParserRule, accept: ValidationAcceptor): void;
|
||||
private checkOperatorMultiplicitiesForMultiAssignmentsIndependent;
|
||||
private checkOperatorMultiplicitiesForMultiAssignmentsNested;
|
||||
checkInterfacePropertyTypes(interfaceDecl: ast.Interface, accept: ValidationAcceptor): void;
|
||||
protected createMixedTypeError(propName: string): string;
|
||||
checkTerminalRuleReturnType(rule: ast.TerminalRule, accept: ValidationAcceptor): void;
|
||||
checkRuleCallParameters(ruleCall: ast.RuleCall, accept: ValidationAcceptor): void;
|
||||
private checkNamedArguments;
|
||||
checkCrossRefNameAssignment(reference: ast.CrossReference, accept: ValidationAcceptor): void;
|
||||
checkCrossRefTerminalType(reference: ast.CrossReference, accept: ValidationAcceptor): void;
|
||||
checkCrossRefType(reference: ast.CrossReference, accept: ValidationAcceptor): void;
|
||||
checkCrossReferenceToTypeUnion(reference: ast.CrossReference, accept: ValidationAcceptor): void;
|
||||
checkFragmentsInTypes(type: ast.SimpleType, accept: ValidationAcceptor): void;
|
||||
checkReferenceTypeUnion(type: ast.ReferenceType, accept: ValidationAcceptor): void;
|
||||
protected checkReferenceToRuleButNotType(type: Reference<ast.AbstractType>): string | undefined;
|
||||
checkAssignmentWithFeatureName(assignment: ast.Assignment, accept: ValidationAcceptor): void;
|
||||
checkPredicateNotSupported(node: ast.Assignment | ast.Group | ast.Keyword | ast.RuleCall, accept: ValidationAcceptor): void;
|
||||
}
|
||||
//# sourceMappingURL=validator.d.ts.map
|
||||
+1
File diff suppressed because one or more lines are too long
+1343
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user