Skip to content

Commit

Permalink
Add filterModelProperties and getEffectiveModelType primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
nguerrera committed May 4, 2022
1 parent 30043d8 commit 0ac9695
Show file tree
Hide file tree
Showing 6 changed files with 520 additions and 6 deletions.
136 changes: 134 additions & 2 deletions packages/compiler/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ export interface Checker {
value: string | number | boolean,
node?: StringLiteralNode | NumericLiteralNode | BooleanLiteralNode
): StringLiteralType | NumericLiteralType | BooleanLiteralType;
getEffectiveModelType(model: ModelType): ModelType;
filterModelProperties(
model: ModelType,
filter: (property: ModelTypeProperty) => boolean
): ModelType;

errorType: ErrorType;
voidType: VoidType;
Expand Down Expand Up @@ -298,6 +303,8 @@ export function createChecker(program: Program): Checker {
createFunctionType,
createLiteralType,
finishType,
getEffectiveModelType,
filterModelProperties,
};

const projectionMembers = createProjectionMembers(checker);
Expand Down Expand Up @@ -873,7 +880,10 @@ export function createChecker(program: Program): Checker {
continue;
}

const newPropType = cloneType(prop, { sourceProperty: prop, model: intersection });
const newPropType = cloneType(prop, {
sourceProperty: prop,
model: intersection,
});
properties.set(prop.name, newPropType);
}
}
Expand Down Expand Up @@ -1701,7 +1711,10 @@ export function createChecker(program: Program): Checker {

// copy each property
for (const prop of walkPropertiesInherited(targetType)) {
const newProp = cloneType(prop, { sourceProperty: prop, model: parentModel });
const newProp = cloneType(prop, {
sourceProperty: prop,
model: parentModel,
});
props.push(newProp);
}
}
Expand All @@ -1718,6 +1731,18 @@ export function createChecker(program: Program): Checker {
}
}

function countPropertiesInherited(model: ModelType) {
let current: ModelType | undefined = model;
let count = 0;

while (current) {
count += current.properties.size;
current = current.baseModel;
}

return count;
}

function checkModelProperty(prop: ModelPropertyNode, parentModel?: ModelType): ModelTypeProperty {
const decorators = checkDecorators(prop);
const valueType = getTypeForNode(prop.value);
Expand Down Expand Up @@ -3116,6 +3141,99 @@ export function createChecker(program: Program): Checker {

return parts.reverse().join(".");
}

function getEffectiveModelType(model: ModelType): ModelType {
while (true) {
if (model.name) {
// named model
return model;
}

// We would need to change the algorithm if this doesn't hold. We
// assume model has no inherited properties below.
compilerAssert(!model.baseModel, "Anonymous model with base model.");

if (model.properties.size === 0) {
// empty model
return model;
}

let source: ModelType | undefined;

for (const property of model.properties.values()) {
const propertySource = getRootSourceModel(property);
if (!propertySource) {
// unsourced property
return model;
}

if (!source) {
// initialize common source from first sourced property.
source = propertySource;
continue;
}

if (isDerivedFrom(source, propertySource)) {
// OK
} else if (isDerivedFrom(propertySource, source)) {
// OK, but refine common source to derived type.
source = propertySource;
} else {
// different source
return model;
}
}

compilerAssert(source, "Should have found a common source to reach here.");

if (model.properties.size !== countPropertiesInherited(source)) {
// source has additional properties.
return model;
}

// keep going until we reach a model that cannot be further reduced.
model = source;
}
}

function filterModelProperties(
model: ModelType,
filter: (property: ModelTypeProperty) => boolean
): ModelType {
let filtered = false;
for (const property of walkPropertiesInherited(model)) {
if (!filter(property)) {
filtered = true;
break;
}
}

if (!filtered) {
return model;
}

const properties = new Map<string, ModelTypeProperty>();
const newModel: ModelType = createType({
kind: "Model",
node: undefined,
name: "",
properties,
decorators: [],
derivedModels: [],
});

for (const property of walkPropertiesInherited(model)) {
if (filter(property)) {
const newProperty = cloneType(property, {
sourceProperty: property,
model: newModel,
});
properties.set(property.name, newProperty);
}
}

return finishType(newModel);
}
}

function isErrorType(type: Type): type is ErrorType {
Expand All @@ -3125,3 +3243,17 @@ function isErrorType(type: Type): type is ErrorType {
function createUsingSymbol(symbolSource: Sym): Sym {
return { flags: SymbolFlags.Using, declarations: [], name: symbolSource.name, symbolSource };
}

function isDerivedFrom(derived: ModelType, base: ModelType) {
while (derived !== base && derived.baseModel) {
derived = derived.baseModel;
}
return derived === base;
}

function getRootSourceModel(property: ModelTypeProperty): ModelType | undefined {
while (property.sourceProperty) {
property = property.sourceProperty;
}
return property?.model;
}
4 changes: 2 additions & 2 deletions packages/compiler/core/projector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ export function createProjector(
*/
function shouldFinishType(type: ModelType | InterfaceType | UnionType) {
if (
type.node.kind !== SyntaxKind.ModelStatement &&
type.node.kind !== SyntaxKind.InterfaceStatement
type.node?.kind !== SyntaxKind.ModelStatement &&
type.node?.kind !== SyntaxKind.InterfaceStatement
) {
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/core/semantic-walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ function navigateType(
*/
export function isTemplate(model: ModelType): boolean {
return (
model.node.kind === SyntaxKind.ModelStatement &&
model.node?.kind === SyntaxKind.ModelStatement &&
model.node.templateParameters.length > 0 &&
!model.templateArguments?.length
);
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export type IntrinsicModel<T extends IntrinsicModelName = IntrinsicModelName> =
export interface ModelType extends BaseType, DecoratedType, TemplatedType {
kind: "Model";
name: IntrinsicModelName | string;
node:
node?:
| ModelStatementNode
| ModelExpressionNode
| IntersectionExpressionNode
Expand Down
Loading

0 comments on commit 0ac9695

Please sign in to comment.