-
Notifications
You must be signed in to change notification settings - Fork 25
Add support for qualified type names (MemberType) to macro #119
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
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ajevans99
approved these changes
Oct 30, 2025
Owner
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.
Couple small comments on tests but looks good otherwise. Thanks!
In the future, would love to add some diagnostics for cases like this so that it isn't a silent failure like it is now. #120
Tests/JSONSchemaIntegrationTests/NestedTypeIntegrationTests.swift
Outdated
Show resolved
Hide resolved
Tests/JSONSchemaIntegrationTests/NestedTypeIntegrationTests.swift
Outdated
Show resolved
Hide resolved
f476318 to
65965ce
Compare
Properties with qualified type names like Weather.Condition were previously unsupported, causing them to be silently excluded from schema generation. This adds handling for .memberType in the type information parser to properly generate schema references for nested/qualified type names.
65965ce to
e08052c
Compare
Contributor
Author
|
anything else to address in this PR? |
ajevans99
approved these changes
Nov 3, 2025
Owner
Nope. Lgtm! |
ajevans99
pushed a commit
that referenced
this pull request
Nov 5, 2025
## Overview This PR adds extensive compile-time validation to the `@Schemable` macro 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. #120 There'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: - We can't check if a type conforms to a protocol (like Schemable) - We can't resolve type aliases - We can't check if a custom type actually exists - We can't do true type checking ## 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'` - Silent generation of invalid JSON schemas that fail at runtime These errors didn't clearly indicate the root cause, making debugging difficult. ## Solution Added three new diagnostic systems: 1. **Initializer Diagnostics** - Validates that the generated schema matches the memberwise initializer 2. **SchemaOptions Diagnostics** - Validates that `@SchemaOptions` and type-specific option macros are used correctly 3. **Unsupported Type Diagnostics** - Warns when properties have unsupported types and will be excluded ## Diagnostics 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. ```swift @Schemable struct Person { let name: String let age: Int = 0 //⚠️ Warning: excluded from init } ``` **Message**: `Property 'age' has a default value which will be excluded from the memberwise initializer` #### 2. Initializer Parameter Order Mismatch (Error) Detects when an explicit initializer has parameters in a different order than the schema expects. ```swift @Schemable struct Person { let name: String let age: Int init(age: Int, name: String) { ... } // ❌ Wrong order } ``` **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. ```swift @Schemable struct Product { let price: Double init(price: Int) { ... } // ❌ Wrong type } ``` **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`. ```swift @Schemable struct Config { let host: String let port: Int @ExcludeFromSchema let internal: Bool init(host: String, port: Int, internal: Bool) { ... } // ❌ Includes excluded property } ``` **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. ```swift @Schemable struct Person { @StringOptions(.minLength(5)) // ❌ String options on Int let age: Int } ``` **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 properties #### 6. Min Greater Than Max Constraints (Error) Detects logically impossible constraint combinations. ```swift @StringOptions(.minLength(10), .maxLength(5)) // ❌ Impossible @NumberOptions(.minimum(100), .maximum(50)) // ❌ Impossible @ArrayOptions(.minItems(10), .maxItems(5)) // ❌ Impossible ``` **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. ```swift @StringOptions(.minLength(-5)) // ❌ Invalid @ArrayOptions(.minItems(-1)) // ❌ Invalid @NumberOptions(.multipleOf(-2)) // ❌ Invalid ``` **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. ```swift @SchemaOptions(.readOnly(true), .writeOnly(true)) // ❌ Impossible let value: String ``` **Message**: `Property 'value' cannot be both readOnly and writeOnly` #### 9. Conflicting Constraint Types (Warning) Warns when both inclusive and exclusive boundary constraints are specified. ```swift @NumberOptions(.minimum(0), .exclusiveMinimum(0)) //⚠️ Conflicting @NumberOptions(.maximum(100), .exclusiveMaximum(100)) //⚠️ Conflicting ``` **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. ```swift @StringOptions(.minLength(5), .minLength(10)) //⚠️ Duplicate ``` **Message**: `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 `@Schemable` macro and will be silently excluded from schema generation. ```swift @Schemable struct Handler { let name: String let callback: () -> Void //⚠️ Function type not supported } ``` **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**: - Function types: `() -> Void`, `(Int) -> String` - Tuple types: `(Int, Int)`, `(x: Int, y: Int)` - Metatypes: `Any.Type`, `String.Type` - Other unsupported Swift types **Why this matters**: This diagnostic would have made [the MemberType bug](#119) (where qualified type names like `Weather.Condition` were silently excluded) much more obvious by warning about the exclusion.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Add support for qualified type names (MemberType)
Description
Fixes a bug where properties with qualified type names were silently excluded from schema generation.
Example:
The macro's type parser didn't handle
.memberTypesyntax. This adds proper support to generate the correct schema reference (Weather.Condition.schema).Type of Change
Additional Notes