namespace SpacetimeDB.Codegen; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; internal static class ErrorDescriptor { private static readonly ErrorDescriptorGroup group = new("STDB", "SpacetimeDB"); public static readonly ErrorDescriptor ReducerReturnType = new( group, "[SpacetimeDB.Reducer] methods must return void", method => $"Reducer method {method.Identifier} returns {method.ReturnType} instead of void.", method => method.ReturnType ); public static readonly ErrorDescriptor AutoIncNotInteger = new( group, "AutoInc fields must be of integer type", field => $"Field {field.Name} is marked as AutoInc but it has a non-integer type {field.Type}.", field => field ); public static readonly ErrorDescriptor UniqueNotEquatable = new( group, "Unique fields must be equatable", field => $"Field {field.Name} is marked as Unique but it has a type {field.Type} which is not an equatable primitive.", field => field ); public static readonly ErrorDescriptor EmptyIndexColumns = new( group, "Index attribute must specify Columns", _ => $"Index attribute doesn't specify columns.", attr => attr ); public static readonly ErrorDescriptor InvalidTableVisibility = new( group, "Table row visibility must be public or internal", table => $"Table {table.Identifier} and its parent types must be public or internal.", table => table.Identifier ); public static readonly ErrorDescriptor TableTaggedEnum = new( group, "Tables cannot be tagged enums", table => $"Table {table.Identifier} is a tagged enum, which is not allowed.", table => table.BaseList! ); public static readonly ErrorDescriptor<( string kind, string exportName, IEnumerable fullNames )> DuplicateExport = new( group, "Duplicate exports", ctx => $"{ctx.kind} with the same export name {ctx.exportName} is registered in multiple places: {string.Join(", ", ctx.fullNames)}", ctx => Location.None ); public static readonly ErrorDescriptor ReducerContextParam = new( group, "Reducers must have a first argument of type ReducerContext", method => $"Reducer method {method.Identifier} does not have a ReducerContext parameter.", method => method.ParameterList ); public static readonly ErrorDescriptor ProcedureContextParam = new( group, "Procedures must have a first argument of type ProcedureContext", method => $"Procedure method {method.Identifier} does not have a ProcedureContext parameter.", method => method.ParameterList ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, string prefix )> ReducerReservedPrefix = new( group, "Reducer method has a reserved name prefix", ctx => $"Reducer method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", ctx => ctx.method.Identifier ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, string prefix )> ProcedureReservedPrefix = new( group, "Procedure method has a reserved name prefix", ctx => $"Procedure method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", ctx => ctx.method.Identifier ); public static readonly UnusedErrorDescriptor IncompatibleTableSchedule = new(group); public static readonly ErrorDescriptor<( ReducerKind kind, IEnumerable fullNames )> DuplicateSpecialReducer = new( group, "Multiple reducers of the same kind", ctx => $"Several reducers are assigned to the same lifecycle kind {ctx.kind}: {string.Join(", ", ctx.fullNames)}", ctx => Location.None ); public static readonly ErrorDescriptor<( AttributeData attr, string message )> InvalidScheduledDeclaration = new(group, "Invalid scheduled table declaration", ctx => $"{ctx.message}", ctx => ctx.attr); public static readonly ErrorDescriptor UnexpectedIndexColumns = new( group, "Index attribute on a field must not specify Columns", _ => $"Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter.", attr => attr ); public static readonly ErrorDescriptor<( AttributeData attr, string columnName, string typeName )> UnknownColumn = new( group, "Unknown column", ctx => $"Could not find the specified column {ctx.columnName} in {ctx.typeName}.", ctx => ctx.attr ); public static readonly ErrorDescriptor ClientVisibilityNotFilter = new( group, "ClientVisibilityFilters must be Filters", field => $"Field {field.Name} is marked as ClientVisibilityFilter but it has type {field.Type} which is not SpacetimeDB.Filter", field => field ); public static readonly ErrorDescriptor ClientVisibilityNotPublicStaticReadonly = new( group, "ClientVisibilityFilters must be public static readonly", field => $"Field {field.Name} is marked as [ClientVisibilityFilter] but it is not public static readonly", field => field ); public static readonly ErrorDescriptor IncompatibleDefaultAttributesCombination = new( group, "Invalid Combination: AutoInc, Unique or PrimaryKey cannot have a Default value", field => $"Field {field.Name} contains a default value and has a AutoInc, Unique or PrimaryKey attributes, which is not allowed.", field => field ); public static readonly ErrorDescriptor InvalidDefaultValueType = new( group, "Invalid Default Value Type", field => $"Default value for field {field.Name} cannot be converted to provided type", field => field ); public static readonly ErrorDescriptor InvalidDefaultValueFormat = new( group, "Invalid Default Value Format", field => $"Default value for field {field.Name} has invalid format for provided type ", field => field ); public static readonly ErrorDescriptor ViewContextParam = new( group, "Views must start with ViewContext or AnonymousViewContext", method => $"View method {method.Identifier} must have a first parameter of type ViewContext or AnonymousViewContext.", method => method.ParameterList ); public static readonly ErrorDescriptor ViewMustHaveName = new( group, "Views must have an explicit name.", method => $"View '{method.Identifier}' must have an explicit name.", method => method ); public static readonly ErrorDescriptor ViewInvalidReturn = new( group, "Views must return T?, List, IQuery, or IEnumerable", method => $"View '{method.Identifier}' must return T?, List, IQuery, or IEnumerable.", method => method.ReturnType ); // TODO: Remove once Views support Private: Views must be Public currently public static readonly ErrorDescriptor ViewMustBePublic = new( group, "Views must be public", method => $"View '{method.Identifier}' must have Public = true. Views are always public in SpacetimeDB.", method => method ); // TODO: Remove once Views support arguments: Views must have no arguments beyond the context. public static readonly ErrorDescriptor ViewArgsUnsupported = new( group, "Views must have no arguments beyond the context.", method => $"View '{method.Identifier}' must have no arguments beyond the context. This is a temporary limitation.", method => method ); public static readonly ErrorDescriptor SettingsMustBeConstCaseConversionPolicy = new( group, "[SpacetimeDB.Settings] field must be a const CaseConversionPolicy", field => $"Settings field {field.Name} must be declared as 'public const SpacetimeDB.CaseConversionPolicy ...'.", field => field ); public static readonly ErrorDescriptor> DuplicateSettings = new( group, "Multiple [SpacetimeDB.Settings] declarations", fullNames => $"[SpacetimeDB.Settings] is declared multiple times: {string.Join(", ", fullNames)}", _ => Location.None ); public static readonly ErrorDescriptor TableLevelIndexMissingAccessor = new( group, "Table-level index attributes must specify Accessor", _ => $"Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.", attr => attr ); public static readonly ErrorDescriptor HttpHandlerContextParam = new( group, "HTTP handlers must have a first argument of type HandlerContext", method => $"HTTP handler method {method.Identifier} does not have a HandlerContext parameter.", method => method.ParameterList ); public static readonly ErrorDescriptor HttpHandlerRequestParam = new( group, "HTTP handlers must have a second argument of type HttpRequest", method => $"HTTP handler method {method.Identifier} does not have an HttpRequest parameter.", method => method.ParameterList ); public static readonly ErrorDescriptor HttpHandlerReturnType = new( group, "HTTP handlers must return HttpResponse", method => $"HTTP handler method {method.Identifier} returns {method.ReturnType} instead of HttpResponse.", method => method.ReturnType ); public static readonly ErrorDescriptor HttpRouterSignature = new( group, "HTTP routers must be static parameterless methods returning Router", method => $"HTTP router method {method.Identifier} must be static, parameterless, and return Router.", method => method ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, string prefix )> HttpHandlerReservedPrefix = new( group, "HTTP handler method has a reserved name prefix", ctx => $"HTTP handler method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.", ctx => ctx.method.Identifier ); public static readonly ErrorDescriptor> DuplicateHttpRouters = new( group, "Multiple [SpacetimeDB.HttpRouter] declarations", fullNames => $"[SpacetimeDB.HttpRouter] is declared multiple times: {string.Join(", ", fullNames)}", _ => Location.None ); public static readonly ErrorDescriptor HttpHandlerSignature = new( group, "HTTP handlers must be non-generic methods with exactly two parameters", method => $"HTTP handler method {method.Identifier} must be non-generic and take exactly two parameters.", method => method.ParameterList ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, SyntaxNode primaryKeySyntax, string primaryKey, string rowType )> ViewPrimaryKeyColumnNotFound = new( group, "View primary key column not found", ctx => $"View '{ctx.method.Identifier}' declares primary key '{ctx.primaryKey}', but row type '{ctx.rowType}' does not have a field with that source name.", ctx => ctx.primaryKeySyntax ); public static readonly ErrorDescriptor<( MethodDeclarationSyntax method, SyntaxNode primaryKeySyntax, string primaryKey, string type )> ViewPrimaryKeyNotFilterable = new( group, "View primary key column type is not supported", ctx => $"View '{ctx.method.Identifier}' declares primary key '{ctx.primaryKey}', but its type '{ctx.type}' is not supported for view primary keys.", ctx => ctx.primaryKeySyntax ); }