@@ -210,8 +210,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
210210 var node = typeContext . TargetNode ;
211211
212212 // Process each [GenerateAdapter] attribute on the host
213- foreach ( var attr in typeContext . Attributes . Where ( a =>
214- a . AttributeClass ? . ToDisplayString ( ) == "PatternKit.Generators.Adapter.GenerateAdapterAttribute" ) )
213+ var generateAdapterAttributes = typeContext . Attributes
214+ . Where ( a => a . AttributeClass ? . ToDisplayString ( ) == "PatternKit.Generators.Adapter.GenerateAdapterAttribute" ) ;
215+
216+ foreach ( var attr in generateAdapterAttributes )
215217 {
216218 GenerateAdapterForAttribute ( spc , hostSymbol , attr , node , typeContext . SemanticModel , generatedAdapters ) ;
217219 }
@@ -307,8 +309,9 @@ private void GenerateAdapterForAttribute(
307309 accessibility == Accessibility . ProtectedOrInternal )
308310 return true ;
309311
310- // Internal constructors are only accessible if in the same assembly
311- if ( accessibility == Accessibility . Internal )
312+ // Internal and private protected constructors are only accessible within the same assembly
313+ if ( accessibility == Accessibility . Internal ||
314+ accessibility == Accessibility . ProtectedAndInternal )
312315 return semanticModel . Compilation . IsSymbolAccessibleWithin ( c , semanticModel . Compilation . Assembly ) ;
313316
314317 return false ;
@@ -500,7 +503,18 @@ private static bool IsValidAdapteeType(INamedTypeSymbol type)
500503 private static bool HasTypeNameConflict ( Compilation compilation , string ns , string typeName )
501504 {
502505 var fullName = string . IsNullOrEmpty ( ns ) ? typeName : $ "{ ns } .{ typeName } ";
503- return compilation . GetTypeByMetadataName ( fullName ) is not null ;
506+ var existingType = compilation . GetTypeByMetadataName ( fullName ) ;
507+ if ( existingType is null )
508+ return false ;
509+
510+ // If the type comes from metadata only, we can't add a partial declaration safely.
511+ if ( existingType . DeclaringSyntaxReferences . Length == 0 )
512+ return true ;
513+
514+ // Only treat this as a conflict if any declaration is non-partial.
515+ return existingType . DeclaringSyntaxReferences . Any ( syntaxRef =>
516+ syntaxRef . GetSyntax ( ) is not TypeDeclarationSyntax typeDecl ||
517+ ! typeDecl . Modifiers . Any ( SyntaxKind . PartialKeyword ) ) ;
504518 }
505519
506520 private List < Diagnostic > ValidateTargetMembers ( INamedTypeSymbol targetType , Location fallbackLocation )
@@ -729,7 +743,15 @@ private static List<ISymbol> GetTargetMembers(INamedTypeSymbol targetType)
729743
730744 var membersToProcess = type . GetMembers ( )
731745 . Where ( m => ! m . IsStatic ) // Exclude static members
732- . Where ( m => ! isAbstractClass || m . IsAbstract ) ;
746+ . Where ( m =>
747+ {
748+ // For abstract classes and interfaces, only include abstract members
749+ // (interfaces: exclude default implementations added in C# 8.0+)
750+ if ( isAbstractClass || type . TypeKind == TypeKind . Interface )
751+ return m . IsAbstract ;
752+
753+ return true ;
754+ } ) ;
733755
734756 foreach ( var member in membersToProcess )
735757 {
@@ -768,18 +790,19 @@ private static List<ISymbol> GetTargetMembers(INamedTypeSymbol targetType)
768790 // This provides both readable (contract-ordered) output and deterministic ordering
769791 return members . OrderBy ( m =>
770792 {
771- // Try to get syntax declaration order (file path + line number)
793+ // Try to get syntax declaration order by line number
772794 var syntaxRef = m . DeclaringSyntaxReferences . FirstOrDefault ( ) ;
773795 if ( syntaxRef != null )
774796 {
775797 var location = syntaxRef . GetSyntax ( ) . GetLocation ( ) ;
776798 var lineSpan = location . GetLineSpan ( ) ;
777- // Return a tuple of (file path, line number) for natural ordering
778- return ( lineSpan . Path , lineSpan . StartLinePosition . Line , 0 ) ;
799+ // Use only line number for ordering, not file path (which varies across machines)
800+ // Note: For types split across multiple partial files, this may not preserve
801+ // perfect declaration order, but ThenBy clauses provide stable fallback ordering
802+ return lineSpan . StartLinePosition . Line ;
779803 }
780804 // For metadata-only symbols without source, use a fallback ordering
781- // Use the symbol's metadata token which is stable across compilations
782- return ( string . Empty , int . MaxValue , m . MetadataToken ) ;
805+ return int . MaxValue ;
783806 } )
784807 . ThenBy ( m => m . Kind )
785808 . ThenBy ( m => m . Name )
@@ -1067,6 +1090,23 @@ private static string GetDefaultValue(IParameterSymbol param)
10671090 return " = default" ;
10681091 }
10691092
1093+ // Handle enum parameters specially to emit proper enum syntax
1094+ if ( param . Type . TypeKind == TypeKind . Enum && param . Type is INamedTypeSymbol enumType )
1095+ {
1096+ // Try to find the enum field matching this value
1097+ var enumField = enumType . GetMembers ( )
1098+ . OfType < IFieldSymbol > ( )
1099+ . FirstOrDefault ( f => f . HasConstantValue && Equals ( f . ConstantValue , value ) ) ;
1100+
1101+ if ( enumField != null )
1102+ {
1103+ return $ " = { enumType . ToDisplayString ( FullyQualifiedFormat ) } .{ enumField . Name } ";
1104+ }
1105+
1106+ // Fallback: cast the numeric value
1107+ return $ " = ({ enumType . ToDisplayString ( FullyQualifiedFormat ) } ){ value } ";
1108+ }
1109+
10701110 var literal = SymbolDisplay . FormatPrimitive ( value , quoteStrings : true , useHexadecimalNumbers : false ) ;
10711111 return " = " + literal ;
10721112 }
0 commit comments