-
Notifications
You must be signed in to change notification settings - Fork 25
Add Diagnostics for @Schemable Macro #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Diagnostics for @Schemable Macro #122
Conversation
This change adds compile-time validation to catch common issues where the generated schema doesn't match the memberwise initializer, preventing confusing compiler errors. Diagnostics implemented: - Warning when properties have default values (excluded from init) - Error when explicit init has wrong parameter order - Error when explicit init has parameter type mismatches - Error when no matching init is found for schema Key changes: - New InitializerDiagnostics.swift with all validation logic - SchemaGenerator now emits diagnostics during macro expansion - Fixed bug where inline comments were captured in default values - Comprehensive tests using assertMacroExpansion All diagnostics use clear, actionable error messages that explain the problem and suggest fixes.
This change adds compile-time validation for @SchemaOptions and type-specific option macros (@StringOptions, @NumberOptions, @ArrayOptions, @ObjectOptions) to catch configuration errors before they create invalid schemas. Diagnostics implemented: - Error when type-specific options don't match property type (e.g., @StringOptions on Int property) - Error when min > max constraints (minLength > maxLength, etc.) - Error when constraint values are negative (minLength < 0) - Error when property is both readOnly and writeOnly - Warning when conflicting constraint types are used (minimum + exclusiveMinimum) - Warning when same option is specified multiple times Key changes: - New SchemaOptionsDiagnostics.swift with all validation logic - SchemableMember.validateOptions() validates options during macro expansion - SchemaGenerator calls validation for each member - Comprehensive tests covering all diagnostic scenarios All validations prevent generation of invalid JSON schemas while providing clear, actionable error messages.
This change adds a warning diagnostic when properties have types that are not supported by the @Schemable macro and will be silently excluded from schema generation. The diagnostic catches: - Function types: () -> Void, (Int) -> String, etc. - Tuple types: (Int, Int), (x: Int, y: Int), etc. - Metatypes: Any.Type, String.Type, etc. - Other unsupported Swift types This prevents silent failures where properties disappear from the schema without warning, which would have made issues like the MemberType bug (qualified type names like Weather.Condition) much more obvious. Key changes: - SchemableMember.generateSchema() now accepts optional context parameter - Emits warning when type.typeInformation() returns .notSupported - SchemaGenerator passes context to generateSchema() - New UnsupportedTypeDiagnostic with clear, actionable message - 5 comprehensive tests covering various unsupported type scenarios Example diagnostic: Property 'callback' has type '() -> Void' which is not supported by the @Schemable macro. This property will be excluded from the generated schema, which may cause the schema to not match the memberwise initializer. This complements existing initializer diagnostics by catching silent schema generation issues at compile time.
The keyword 'internal' is reserved in Swift and cannot be used as an identifier. Renamed test property to 'internalFlag' to fix compilation errors in CI.
The .default() call should be at the same indentation level as the component above it (e.g., JSONString()), not indented 2 spaces further. This matches the existing test expectations in SchemableExpansionTests and fixes the 20 test failures on CI in InitializerDiagnosticsTests.
- Fix SchemaOptionsGenerator to generate method calls at the same indentation level as the component (not 2 spaces more) - Fix test expectations in SchemaOptionsDiagnosticsTests to match correct indentation - Remove attribute macros from expandedSource (they should be consumed during expansion) - Fix readOnly/writeOnly placement to be inside JSONProperty closure This brings SchemaOptions behavior in line with the existing tests in SchemaOptionsTests.swift.
- Only emit default value warnings for mixed cases (some properties with defaults, some without) rather than all cases with defaults - Fix findMatchingInit to match by parameter name set regardless of order, then validate order separately - Only emit type mismatch errors when parameter names match at that position (avoid spurious errors when order is wrong) - Update test expectations to include .default() in schema output - Rename test to reflect corrected behavior (no diagnostics when all properties have defaults) - Fix diagnostic location expectation This eliminates false positive warnings while still catching real initializer/schema mismatches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances the @Schemable macro with comprehensive diagnostic capabilities to help developers identify configuration issues at compile-time. The changes add validation for unsupported types, schema options mismatches, and initializer compatibility problems.
- Adds diagnostics for unsupported property types (functions, tuples, metatypes)
- Implements validation for schema options (type mismatches, constraint logic errors, negative values)
- Adds initializer mismatch detection to ensure schemas match memberwise initializers
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| UnsupportedTypeDiagnosticsTests.swift | Tests for diagnostic warnings when properties have unsupported types |
| SimpleDiagnosticsTests.swift | Basic smoke test for macro instantiation |
| SchemaOptionsDiagnosticsTests.swift | Comprehensive tests for schema option validation diagnostics |
| InitializerDiagnosticsTests.swift | Tests for initializer parameter matching and default value diagnostics |
| MacroExpansion+SwiftTesting.swift | Adds diagnostics parameter to test helper function |
| SchemableMember.swift | Adds option validation and unsupported type diagnostics |
| SchemableMacro.swift | Threads MacroExpansionContext through to enable diagnostics |
| SchemaOptionsDiagnostics.swift | New file implementing schema options validation logic |
| SchemaGenerator.swift | Integrates diagnostics validation into schema generation |
| InitializerDiagnostics.swift | New file implementing initializer matching validation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ature/initializer-diagnostics
The isExcluded field was never accessed after being set, and the logic was confusing due to the misleading name of shouldExcludeFromSchema. Since excludedProperties is computed by filtering, the field is redundant.
Add explicit dependencies for: - JSONSchemaMacro: SwiftBasicFormat, SwiftDiagnostics, SwiftSyntaxBuilder - JSONSchemaMacroTests: SwiftParser, SwiftParserDiagnostics, SwiftBasicFormat, SwiftDiagnostics These are required by the diagnostic functionality added in the initializer and schema options diagnostics features.
ajevans99
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool
During the merge from main, the isStatic extension on VariableDeclSyntax and the static property filter in schemableMembers() were accidentally lost. This commit restores: - isStatic computed property on VariableDeclSyntax - Static property filtering in schemableMembers() - Missing comma in SchemableMember.generateSchema parameter list (from merge conflict)
Overview
This PR adds extensive compile-time validation to the
@Schemablemacro to catch common configuration errors before they result in confusing compiler errors or invalid JSON schemas. All diagnostics emit clear, actionable error messages during macro expansion. #120There's some limitations in macros that don't allow us to cover the full spectrum of possible diagnostic messages, as macros can only see the syntax tree, not the semantic type system.
What this means:
Problem Statement
Previously, when the generated schema didn't match the memberwise initializer or when schema options were misconfigured, users would encounter confusing compiler errors like:
cannot convert value of type '(String, Int) -> Person' to expected argument type '@Sendable ((String)) -> Person'type 'X' has no member 'schema'These errors didn't clearly indicate the root cause, making debugging difficult.
Solution
Added three new diagnostic systems:
@SchemaOptionsand type-specific option macros are used correctlyDiagnostics Added (11 Total)
Initializer Diagnostics (4)
1. Property with Default Value (Warning)
Detects when properties have default values that will be excluded from the synthesized memberwise initializer.
Message:
Property 'age' has a default value which will be excluded from the memberwise initializer2. Initializer Parameter Order Mismatch (Error)
Detects when an explicit initializer has parameters in a different order than the schema expects.
Message:
Initializer parameter at position 1 is 'age' but schema expects 'name'. The schema will generate properties in a different order than the initializer parameters.3. Initializer Parameter Type Mismatch (Error)
Detects when parameter types don't match property types.
Message:
Parameter 'price' has type 'Int' but schema expects 'Double'. This type mismatch will cause the generated schema to fail.4. No Matching Initializer (Error)
Detects when a type has explicit initializers but none match the schema signature, especially when using
@ExcludeFromSchema.Message: Detailed message showing expected signature, available initializers, and excluded properties with suggestions.
SchemaOptions Diagnostics (6)
5. Type Mismatch for Option Macros (Error)
Detects when type-specific option macros are used on incompatible property types.
Message:
@StringOptions can only be used on String properties, but 'age' has type 'Int'Applies to:
@StringOptions→ must be used on String properties@NumberOptions→ must be used on numeric properties (Int, Double, etc.)@ArrayOptions→ must be used on Array properties6. Min Greater Than Max Constraints (Error)
Detects logically impossible constraint combinations.
Message:
Property 'username' has minLength (10) greater than maxLength (5). This string length constraint can never be satisfied.7. Negative Constraint Values (Error)
Detects invalid negative values for size/length constraints.
Message:
Property 'text' has minLength with negative value (-5). This constraint must be non-negative.8. ReadOnly and WriteOnly Conflict (Error)
Detects when a property is marked as both read-only and write-only.
Message:
Property 'value' cannot be both readOnly and writeOnly9. Conflicting Constraint Types (Warning)
Warns when both inclusive and exclusive boundary constraints are specified.
Message:
Property 'value' has both minimum and exclusiveMinimum specified. Use only one of minimum or exclusiveMinimum.10. Duplicate Options (Warning)
Warns when the same option is specified multiple times.
@StringOptions(.minLength(5), .minLength(10)) // ⚠️ DuplicateMessage:
Property 'text' has minLength specified 2 times. Only the last value will be used.Unsupported Type Diagnostics (1)
11. Unsupported Property Type (Warning)
Warns when properties have types that are not supported by the
@Schemablemacro and will be silently excluded from schema generation.Message:
Property 'callback' has type '() -> Void' which is not supported by the @Schemable macro. This property will be excluded from the generated schema, which may cause the schema to not match the memberwise initializer.Catches:
() -> Void,(Int) -> String(Int, Int),(x: Int, y: Int)Any.Type,String.TypeWhy this matters: This diagnostic would have made the MemberType bug (where qualified type names like
Weather.Conditionwere silently excluded) much more obvious by warning about the exclusion.