11using Microsoft . CodeAnalysis ;
22using Microsoft . CodeAnalysis . CSharp ;
33using Microsoft . CodeAnalysis . CSharp . Syntax ;
4- using System . Collections . Immutable ;
54using System . Text ;
65
76namespace PatternKit . Generators . Adapter ;
@@ -35,6 +34,10 @@ public sealed class AdapterGenerator : IIncrementalGenerator
3534 private const string DiagIdOverloadedMethodsNotSupported = "PKADP011" ;
3635 private const string DiagIdAbstractClassNoParameterlessCtor = "PKADP012" ;
3736 private const string DiagIdSettablePropertiesNotSupported = "PKADP013" ;
37+ private const string DiagIdNestedOrGenericHost = "PKADP014" ;
38+ private const string DiagIdMappingMethodNotAccessible = "PKADP015" ;
39+ private const string DiagIdStaticMembersNotSupported = "PKADP016" ;
40+ private const string DiagIdRefReturnNotSupported = "PKADP017" ;
3841
3942 private static readonly DiagnosticDescriptor HostNotStaticPartialDescriptor = new (
4043 id : DiagIdHostNotStaticPartial ,
@@ -140,6 +143,38 @@ public sealed class AdapterGenerator : IIncrementalGenerator
140143 defaultSeverity : DiagnosticSeverity . Error ,
141144 isEnabledByDefault : true ) ;
142145
146+ private static readonly DiagnosticDescriptor NestedOrGenericHostDescriptor = new (
147+ id : DiagIdNestedOrGenericHost ,
148+ title : "Nested or generic host not supported" ,
149+ messageFormat : "Adapter host '{0}' cannot be nested or generic" ,
150+ category : "PatternKit.Generators.Adapter" ,
151+ defaultSeverity : DiagnosticSeverity . Error ,
152+ isEnabledByDefault : true ) ;
153+
154+ private static readonly DiagnosticDescriptor MappingMethodNotAccessibleDescriptor = new (
155+ id : DiagIdMappingMethodNotAccessible ,
156+ title : "Mapping method must be accessible" ,
157+ messageFormat : "Mapping method '{0}' must be public or internal to be accessible from generated adapter" ,
158+ category : "PatternKit.Generators.Adapter" ,
159+ defaultSeverity : DiagnosticSeverity . Error ,
160+ isEnabledByDefault : true ) ;
161+
162+ private static readonly DiagnosticDescriptor StaticMembersNotSupportedDescriptor = new (
163+ id : DiagIdStaticMembersNotSupported ,
164+ title : "Static members are not supported" ,
165+ messageFormat : "Target type '{0}' contains static member '{1}' which is not supported by the adapter generator" ,
166+ category : "PatternKit.Generators.Adapter" ,
167+ defaultSeverity : DiagnosticSeverity . Error ,
168+ isEnabledByDefault : true ) ;
169+
170+ private static readonly DiagnosticDescriptor RefReturnNotSupportedDescriptor = new (
171+ id : DiagIdRefReturnNotSupported ,
172+ title : "Ref-return members are not supported" ,
173+ messageFormat : "Target type '{0}' contains ref-return member '{1}' which is not supported by the adapter generator" ,
174+ category : "PatternKit.Generators.Adapter" ,
175+ defaultSeverity : DiagnosticSeverity . Error ,
176+ isEnabledByDefault : true ) ;
177+
143178 public void Initialize ( IncrementalGeneratorInitializationContext context )
144179 {
145180 // Find all class declarations with [GenerateAdapter] attribute
@@ -183,6 +218,16 @@ private void GenerateAdapterForAttribute(
183218 return ;
184219 }
185220
221+ // Validate host is not nested or generic
222+ if ( hostSymbol . ContainingType is not null || hostSymbol . TypeParameters . Length > 0 )
223+ {
224+ context . ReportDiagnostic ( Diagnostic . Create (
225+ NestedOrGenericHostDescriptor ,
226+ node . GetLocation ( ) ,
227+ hostSymbol . Name ) ) ;
228+ return ;
229+ }
230+
186231 // Parse attribute arguments
187232 var config = ParseAdapterConfig ( attribute ) ;
188233 if ( config . TargetType is null || config . AdapteeType is null )
@@ -240,7 +285,7 @@ private void GenerateAdapterForAttribute(
240285 // Get all mapping methods from host
241286 var mappingMethods = GetMappingMethods ( hostSymbol , config . AdapteeType ) ;
242287
243- // Validate mapping methods are static
288+ // Validate mapping methods are static and accessible
244289 foreach ( var ( method , _) in mappingMethods )
245290 {
246291 if ( ! method . IsStatic )
@@ -251,6 +296,17 @@ private void GenerateAdapterForAttribute(
251296 method . Name ) ) ;
252297 return ;
253298 }
299+
300+ // Validate method is accessible (public or internal)
301+ if ( method . DeclaredAccessibility != Accessibility . Public &&
302+ method . DeclaredAccessibility != Accessibility . Internal )
303+ {
304+ context . ReportDiagnostic ( Diagnostic . Create (
305+ MappingMethodNotAccessibleDescriptor ,
306+ method . Locations . FirstOrDefault ( ) ?? node . GetLocation ( ) ,
307+ method . Name ) ) ;
308+ return ;
309+ }
254310 }
255311
256312 // Get target members that need mapping
@@ -403,6 +459,16 @@ private List<Diagnostic> ValidateTargetMembers(INamedTypeSymbol targetType, Loca
403459
404460 foreach ( var member in membersToCheck )
405461 {
462+ // Check for static members (not supported)
463+ if ( member . IsStatic )
464+ {
465+ diagnostics . Add ( Diagnostic . Create (
466+ StaticMembersNotSupportedDescriptor ,
467+ location ,
468+ targetType . Name ,
469+ member . Name ) ) ;
470+ }
471+
406472 // Check for events (not supported)
407473 if ( member is IEventSymbol evt )
408474 {
@@ -423,6 +489,16 @@ private List<Diagnostic> ValidateTargetMembers(INamedTypeSymbol targetType, Loca
423489 prop . Name ) ) ;
424490 }
425491
492+ // Check for ref-return properties (not supported)
493+ if ( member is IPropertySymbol refProp && refProp . ReturnsByRef )
494+ {
495+ diagnostics . Add ( Diagnostic . Create (
496+ RefReturnNotSupportedDescriptor ,
497+ location ,
498+ targetType . Name ,
499+ refProp . Name ) ) ;
500+ }
501+
426502 // Check for generic methods (not supported)
427503 if ( member is IMethodSymbol method && method . MethodKind == MethodKind . Ordinary )
428504 {
@@ -435,6 +511,16 @@ private List<Diagnostic> ValidateTargetMembers(INamedTypeSymbol targetType, Loca
435511 method . Name ) ) ;
436512 }
437513
514+ // Check for ref-return methods (not supported)
515+ if ( method . ReturnsByRef || method . ReturnsByRefReadonly )
516+ {
517+ diagnostics . Add ( Diagnostic . Create (
518+ RefReturnNotSupportedDescriptor ,
519+ location ,
520+ targetType . Name ,
521+ method . Name ) ) ;
522+ }
523+
438524 // Track full method signature for overload detection
439525 var sig = GetMemberSignature ( method ) ;
440526 if ( ! methodSignatures . TryGetValue ( method . Name , out var sigs ) )
@@ -557,6 +643,7 @@ private static List<ISymbol> GetTargetMembers(INamedTypeSymbol targetType)
557643 continue ;
558644
559645 var membersToProcess = type . GetMembers ( )
646+ . Where ( m => ! m . IsStatic ) // Exclude static members
560647 . Where ( m => ! isAbstractClass || m . IsAbstract ) ;
561648
562649 foreach ( var member in membersToProcess )
@@ -592,8 +679,12 @@ private static List<ISymbol> GetTargetMembers(INamedTypeSymbol targetType)
592679 }
593680 }
594681
595- // Return in declaration order (members already added in traversal order)
596- return members ;
682+ // Ensure stable, deterministic ordering by kind+name+signature
683+ // This provides a predictable output order even if member traversal is non-deterministic
684+ return members . OrderBy ( m => m . Kind )
685+ . ThenBy ( m => m . Name )
686+ . ThenBy ( m => m . ToDisplayString ( FullyQualifiedFormat ) )
687+ . ToList ( ) ;
597688 }
598689
599690 private static string GetMemberSignature ( IMethodSymbol method )
0 commit comments