import { createRule, getDiscriminator, getTypeName, isTemplateInstance, paramMessage, } from '@typespec/compiler' import { SyntaxKind } from '@typespec/compiler/ast' export const compositionOverInheritanceRule = createRule({ name: 'composition-over-inheritance', description: 'Check that if a model is used in an operation and has derived models that it has a discriminator or recommend to use composition via spread or `is`.', severity: 'warning', messages: { default: paramMessage`Model '${'name'}' is extending '${'baseModel'}' that doesn't define a discriminator. If '${'baseModel'}' is meant to be used: - For composition consider using spread \`...\` or \`model is\` instead. - As a polymorphic relation, add the \`@discriminator\` decorator on the base model.`, instance: paramMessage`Model '${'name'}' is extending a template '${'baseModel'}'. Consider using composition with spread \`...\` or \`model is\` instead.`, }, create(context) { return { model: (model) => { if ( model.baseModel && model.node?.kind === SyntaxKind.ModelStatement && model.node.extends && getDiscriminator(context.program, model.baseModel) === undefined ) { context.reportDiagnostic({ messageId: isTemplateInstance(model.baseModel) ? 'instance' : 'default', format: { name: model.name, baseModel: getTypeName(model.baseModel), }, target: model.node.extends, }) } }, } }, })