[sharpie] Emit correct nullability for block/delegate parameters#25837
Conversation
When ObjC declares a block with nullable parameters (e.g., void (^)(BOOL, NSError * _Nullable)), sharpie now produces Action<bool, NSError?> instead of Action<bool, NSError>. This also applies to the return type of Func<> delegates. The logic checks for CX_AttrKind_TypeNullable annotations on resolved types and wraps them with ComposedType.HasNullableSpecifier to produce the '?' suffix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When CustomDelegateMassager or BindingGenerator convert inline Action<>/Func<> types to named delegate declarations, unwrap nullable ComposedTypes (T?) back to their base type. This preserves the annotations so NullabilityMassager can add [NullAllowed] to the delegate parameter instead, which is the correct representation for binding definition delegate parameters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend the NullabilityCheck to inspect block/delegate parameters for nullability mismatches between native and managed code. When a method parameter is a block type (e.g., void (^)(BOOL, NSError *)), the check now validates nullability of each block parameter against the NullableAttribute byte array on the managed generic type arguments. Reports !missing-null-allowed! and !extra-null-allowed! for block parameter nullability mismatches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When ObjC declares a block with nullable parameters (e.g., void (^)(BOOL, NSError * _Nullable)), sharpie now produces Action<bool, NSError?> instead of Action<bool, NSError>. This also applies to the return type of Func<> delegates. The logic checks for CX_AttrKind_TypeNullable annotations on resolved types and wraps them with ComposedType.HasNullableSpecifier to produce the '?' suffix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When CustomDelegateMassager or BindingGenerator convert inline Action<>/Func<> types to named delegate declarations, unwrap nullable ComposedTypes (T?) back to their base type. This preserves the annotations so NullabilityMassager can add [NullAllowed] to the delegate parameter instead, which is the correct representation for binding definition delegate parameters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend the NullabilityCheck to inspect block/delegate parameters for nullability mismatches between native and managed code. When a method parameter is a block type (e.g., void (^)(BOOL, NSError *)), the check now validates nullability of each block parameter against the NullableAttribute byte array on the managed generic type arguments. Reports !missing-null-allowed! and !extra-null-allowed! for block parameter nullability mismatches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…/sharpie-block-nullability
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Update the Nullability test expected output files to reflect the new nullable type arguments in block parameters (Action<NSObject?> instead of Action<NSObject>). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Classify all 2003 block parameter nullability mismatches as known issues. Also change the error message for block parameter nullability to use '?' instead of '[NullAllowed]' since the fix is adding a nullable type argument (e.g. NSError?) rather than an attribute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR improves sharpie’s nullability fidelity for Objective-C blocks by preserving _Nullable annotations on block/delegate parameter types, emitting Action<..., T?>/Func<..., T?> for inline delegates, and extending xtro-sharpie validation to detect nullability mismatches inside block parameters.
Changes:
- Update
TypeBinder.VisitFunctionTypeto wrap block parameter/return generic arguments as nullable (T?) when clang’sTypeNullableattribute is present. - Preserve nullability annotations when converting inline delegates into named delegate declarations so
NullabilityMassagercan emit[NullAllowed]on delegate parameters. - Extend xtro-sharpie
NullabilityCheckto validate nullability for block parameter inner parameters, and update api-annotations ignore baselines accordingly.
Reviewed changes
Copilot reviewed 180 out of 180 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/sharpie/Sharpie.Bind/TypeBinder.cs | Detects TypeNullable on block parameter/return types and emits nullable AstType (?) for inline Action<>/Func<> generic arguments. |
| tools/sharpie/Sharpie.Bind/Massagers/CustomDelegateMassager.cs | Unwraps T? back to T (while preserving annotations) when materializing named delegates so [NullAllowed] can be applied to delegate parameters. |
| tools/sharpie/Sharpie.Bind/BindingGenerator.cs | Applies the same nullable-unwrapping logic when generating delegate declarations from block pointer typedefs. |
| tests/xtro-sharpie/xtro-sharpie/NullabilityCheck.cs | Adds validation that compares native block-parameter nullability vs managed NullableAttribute positions for Action<>/Func<> type arguments. |
| tests/xtro-sharpie/api-annotations-dotnet/*.ignore (multiple files) | Baseline updates for newly-detected block-parameter nullability discrepancies across platforms/frameworks. |
| tests/sharpie/Tests/Nullability.macosx.cs | Updates expected output to Action<NSObject?> for a block whose parameter is declared _Nullable. |
| tests/sharpie/Tests/Nullability.iphoneos.cs | Same as macOS test: expects Action<NSObject?> for nullable block parameter. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Fixes CS0219 warning that would be promoted to an error by TreatWarningsAsErrors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Entries that are identical across all four platforms (iOS, macOS, tvOS, MacCatalyst) are moved from the platform-specific .ignore files into the corresponding common-*.ignore file to reduce duplication. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Delete platform-specific .ignore files that have no remaining entries after moving common entries to common-*.ignore. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
✅ [PR Build #2dce399] Build passed (Detect API changes) ✅Pipeline on Agent |
✅ [PR Build #2dce399] Build passed (Build packages) ✅Pipeline on Agent |
✅ API diff for current PR / commitNET (empty diffs)✅ API diff vs stableNET (empty diffs)ℹ️ Generator diffGenerator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes) Pipeline on Agent |
✅ [PR Build #2dce399] Build passed (Build macOS tests) ✅Pipeline on Agent |
This comment has been minimized.
This comment has been minimized.
🚀 [CI Build #2dce399] Test results 🚀Test results✅ All tests passed on VSTS: test results. 🎉 All 199 tests passed 🎉 Tests counts✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download macOS tests✅ Tests on macOS Monterey (12): All 5 tests passed. Html Report (VSDrops) Download Linux Build VerificationPipeline on Agent |
) When ObjC declares a block with nullable parameters (e.g., `void (^)(BOOL success, NSError * _Nullable error)`), sharpie previously produced `Action<bool, NSError>`, losing the nullability information. This PR teaches sharpie to produce `Action<bool, NSError?>` instead. ## Approach The fix operates at two levels in the sharpie pipeline: - **TypeBinder.VisitFunctionType** -- After resolving each block parameter type, checks for `CX_AttrKind_TypeNullable` in the `AttributedType` annotations (which clang already attaches). If found, wraps the resolved type with `ComposedType.HasNullableSpecifier = true` to produce the `?` suffix in type arguments. This handles the inline `Action<>`/`Func<>` case. - **CustomDelegateMassager + BindingGenerator** -- When converting inline delegates to named delegate declarations, unwraps the nullable `ComposedType` back to its base type while preserving annotations. This allows the existing `NullabilityMassager` to add `[NullAllowed]` to the delegate parameter (the correct representation for binding definition delegates). ## xtro-sharpie validation Extends `NullabilityCheck` to validate block parameter nullability: - Detects `BlockPointerType` parameters and inspects the underlying `FunctionProtoType` - For each block parameter, compares native nullability against the managed `NullableAttribute` byte array positions - Reports `!missing-null-allowed!` / `!extra-null-allowed!` for mismatches This addresses the existing TODO in `NullabilityCheck`: "check the generics (don't think we have such cases yet)". 🤖 Pull request created by Copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolf Bjarne Kvinge <rokvin@microsoft.com>
When ObjC declares a block with nullable parameters (e.g.,
void (^)(BOOL success, NSError * _Nullable error)), sharpie previously producedAction<bool, NSError>, losing the nullability information. This PR teaches sharpie to produceAction<bool, NSError?>instead.Approach
The fix operates at two levels in the sharpie pipeline:
TypeBinder.VisitFunctionType -- After resolving each block parameter type, checks for
CX_AttrKind_TypeNullablein theAttributedTypeannotations (which clang already attaches). If found, wraps the resolved type withComposedType.HasNullableSpecifier = trueto produce the?suffix in type arguments. This handles the inlineAction<>/Func<>case.CustomDelegateMassager + BindingGenerator -- When converting inline delegates to named delegate declarations, unwraps the nullable
ComposedTypeback to its base type while preserving annotations. This allows the existingNullabilityMassagerto add[NullAllowed]to the delegate parameter (the correct representation for binding definition delegates).xtro-sharpie validation
Extends
NullabilityCheckto validate block parameter nullability:BlockPointerTypeparameters and inspects the underlyingFunctionProtoTypeNullableAttributebyte array positions!missing-null-allowed!/!extra-null-allowed!for mismatchesThis addresses the existing TODO in
NullabilityCheck: "check the generics (don't think we have such cases yet)".🤖 Pull request created by Copilot