@@ -1467,7 +1467,7 @@ typeSymbol is INamedTypeSymbol lifecycleType &&
14671467
14681468 if ( selectedConstructor is not null &&
14691469 ( selectedConstructor . Parameters . Length != 0 ||
1470- members . Any ( static member => member . IsInitOnly ) ||
1470+ members . Any ( static member => member . NeedsObjectInitializer ) ||
14711471 extensionData is { IsInitOnly : true } ) )
14721472 {
14731473 EmitReadObjectCoreWithConstructor ( builder , index , ctorType , typeName , selectedConstructor , members , extensionData , indexByType , emitLifecycleCallbacks , propertyNamingPolicy ) ;
@@ -1849,9 +1849,12 @@ private static void EmitReadObjectCoreWithConstructor(
18491849 var bufferedMembers = members
18501850 . Where ( m => IsWritableMember ( m . Symbol ) && ! ctorBoundMembers . Contains ( m . Symbol ) )
18511851 . ToArray ( ) ;
1852- var initOnlyMembers = bufferedMembers . Where ( static m => m . IsInitOnly ) . ToArray ( ) ;
1853- var postCreateBufferedMembers = bufferedMembers . Where ( static m => ! m . IsInitOnly ) . ToArray ( ) ;
1854- var useObjectInitializer = initOnlyMembers . Length != 0 || extensionData is { IsInitOnly : true } ;
1852+ var initializerMembers = bufferedMembers . Where ( static m => m . NeedsObjectInitializer ) . ToArray ( ) ;
1853+ var postCreateBufferedMembers = bufferedMembers . Where ( static m => ! m . NeedsObjectInitializer ) . ToArray ( ) ;
1854+ var useObjectInitializer = initializerMembers . Length != 0 || extensionData is { IsInitOnly : true } ;
1855+ var pureInitOnlyMembers = initializerMembers . Where ( static m => m . IsInitOnly && ! m . IsRequiredKeyword ) . ToArray ( ) ;
1856+ var requiredKeywordInitMembers = initializerMembers . Where ( static m => m . IsRequiredKeyword ) . ToArray ( ) ;
1857+ var needsDefaults = pureInitOnlyMembers . Length != 0 || extensionData is { IsInitOnly : true } ;
18551858 var initOnlyExtensionDataValueVarName = extensionData is { IsInitOnly : true } ? $ "__extensionData{ index } " : null ;
18561859
18571860 var bufferedMemberValueVarNames = new Dictionary < ISymbol , string > ( bufferedMembers . Length , SymbolEqualityComparer . Default ) ;
@@ -2044,7 +2047,8 @@ private static void EmitReadObjectCoreWithConstructor(
20442047 member . AttributeConverterTypeName ,
20452048 member . ObjectCreationHandling ,
20462049 member . IsRequired ,
2047- member . IsInitOnly ) ;
2050+ member . IsInitOnly ,
2051+ member . IsRequiredKeyword ) ;
20482052
20492053 builder . Append ( " if (!matched && global::System.String.Equals(mergeKey, " ) . Append ( member . SerializedNameExpressionForRead )
20502054 . Append ( ", options.PropertyNameCaseInsensitive ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal))" ) ;
@@ -2181,7 +2185,8 @@ private static void EmitReadObjectCoreWithConstructor(
21812185 member . AttributeConverterTypeName ,
21822186 member . ObjectCreationHandling ,
21832187 member . IsRequired ,
2184- member . IsInitOnly ) ;
2188+ member . IsInitOnly ,
2189+ member . IsRequiredKeyword ) ;
21852190
21862191 builder . Append ( " if (!matched && global::System.String.Equals(key, " ) . Append ( member . SerializedNameExpressionForRead )
21872192 . Append ( ", options.PropertyNameCaseInsensitive ? global::System.StringComparison.OrdinalIgnoreCase : global::System.StringComparison.Ordinal))" ) ;
@@ -2267,55 +2272,37 @@ private static void EmitReadObjectCoreWithConstructor(
22672272 builder . AppendLine ( ) ;
22682273 if ( useObjectInitializer )
22692274 {
2270- builder . Append ( " var __defaults" ) . Append ( index ) . Append ( " = default(" ) . Append ( typeName ) ;
2271- if ( typeSymbol . IsReferenceType )
2272- {
2273- builder . Append ( '?' ) ;
2274- }
2275-
2276- builder . AppendLine ( ");" ) ;
2277- if ( extensionData is { IsInitOnly : true } )
2275+ if ( needsDefaults )
22782276 {
2279- builder . Append ( " __defaults" ) . Append ( index ) . Append ( " = new " ) . Append ( typeName ) . Append ( '(' ) ;
2280- for ( var i = 0 ; i < parameters . Length ; i ++ )
2277+ builder . Append ( " var __defaults" ) . Append ( index ) . Append ( " = default( " ) . Append ( typeName ) ;
2278+ if ( typeSymbol . IsReferenceType )
22812279 {
2282- if ( i != 0 )
2283- {
2284- builder . Append ( ", " ) ;
2285- }
2286-
2287- builder . Append ( parameterValueVarNames [ i ] ) ;
2280+ builder . Append ( '?' ) ;
22882281 }
22892282
22902283 builder . AppendLine ( ");" ) ;
22912284 }
2292- else
2285+
2286+ if ( extensionData is { IsInitOnly : true } )
2287+ {
2288+ EmitDefaultsConstruction ( builder , index , typeName , parameters , parameterValueVarNames , requiredKeywordInitMembers , " " ) ;
2289+ }
2290+ else if ( pureInitOnlyMembers . Length != 0 )
22932291 {
22942292 builder . Append ( " if (" ) ;
2295- for ( var i = 0 ; i < initOnlyMembers . Length ; i ++ )
2293+ for ( var i = 0 ; i < pureInitOnlyMembers . Length ; i ++ )
22962294 {
22972295 if ( i != 0 )
22982296 {
22992297 builder . Append ( " || " ) ;
23002298 }
23012299
2302- builder . Append ( '!' ) . Append ( bufferedMemberSeenVarNames [ initOnlyMembers [ i ] . Symbol ] ) ;
2300+ builder . Append ( '!' ) . Append ( bufferedMemberSeenVarNames [ pureInitOnlyMembers [ i ] . Symbol ] ) ;
23032301 }
23042302
23052303 builder . AppendLine ( ")" ) ;
23062304 builder . AppendLine ( " {" ) ;
2307- builder . Append ( " __defaults" ) . Append ( index ) . Append ( " = new " ) . Append ( typeName ) . Append ( '(' ) ;
2308- for ( var i = 0 ; i < parameters . Length ; i ++ )
2309- {
2310- if ( i != 0 )
2311- {
2312- builder . Append ( ", " ) ;
2313- }
2314-
2315- builder . Append ( parameterValueVarNames [ i ] ) ;
2316- }
2317-
2318- builder . AppendLine ( ");" ) ;
2305+ EmitDefaultsConstruction ( builder , index , typeName , parameters , parameterValueVarNames , requiredKeywordInitMembers , " " ) ;
23192306 builder . AppendLine ( " }" ) ;
23202307 }
23212308
@@ -2404,19 +2391,31 @@ private static void EmitReadObjectCoreWithConstructor(
24042391 {
24052392 builder . AppendLine ( ")" ) ;
24062393 builder . AppendLine ( " {" ) ;
2407- for ( var i = 0 ; i < initOnlyMembers . Length ; i ++ )
2394+ for ( var i = 0 ; i < initializerMembers . Length ; i ++ )
24082395 {
2409- var member = initOnlyMembers [ i ] ;
2396+ var member = initializerMembers [ i ] ;
24102397 var seenVar = bufferedMemberSeenVarNames [ member . Symbol ] ;
24112398 var valueVar = bufferedMemberValueVarNames [ member . Symbol ] ;
24122399
2413- builder . Append ( " " ) . Append ( member . Symbol . Name ) . Append ( " = " ) . Append ( seenVar ) . Append ( " ? " ) . Append ( valueVar ) . Append ( " : __defaults" ) . Append ( index ) ;
2414- if ( typeSymbol . IsReferenceType )
2400+ builder . Append ( " " ) . Append ( member . Symbol . Name ) . Append ( " = " ) . Append ( seenVar ) . Append ( " ? " ) . Append ( valueVar ) ;
2401+ if ( member . IsRequiredKeyword )
24152402 {
2416- builder . Append ( '!' ) ;
2403+ // Required-keyword members: if not seen, the required-member validation
2404+ // will throw before this value is observed. Use default! as a safe placeholder.
2405+ builder . Append ( " : default!" ) ;
24172406 }
2407+ else
2408+ {
2409+ builder . Append ( " : __defaults" ) . Append ( index ) ;
2410+ if ( typeSymbol . IsReferenceType )
2411+ {
2412+ builder . Append ( '!' ) ;
2413+ }
24182414
2419- builder . Append ( '.' ) . Append ( member . Symbol . Name ) . AppendLine ( "," ) ;
2415+ builder . Append ( '.' ) . Append ( member . Symbol . Name ) ;
2416+ }
2417+
2418+ builder . AppendLine ( "," ) ;
24202419 }
24212420
24222421 if ( extensionData is { IsInitOnly : true } initOnlyExtensionData )
@@ -2581,6 +2580,43 @@ private static void EmitReadObjectCoreWithConstructor(
25812580 builder . AppendLine ( " return instance;" ) ;
25822581 }
25832582
2583+ private static void EmitDefaultsConstruction (
2584+ StringBuilder builder ,
2585+ int index ,
2586+ string typeName ,
2587+ ImmutableArray < IParameterSymbol > parameters ,
2588+ string [ ] parameterValueVarNames ,
2589+ MemberModel [ ] requiredKeywordInitMembers ,
2590+ string indent )
2591+ {
2592+ builder . Append ( indent ) . Append ( "__defaults" ) . Append ( index ) . Append ( " = new " ) . Append ( typeName ) . Append ( '(' ) ;
2593+ for ( var i = 0 ; i < parameters . Length ; i ++ )
2594+ {
2595+ if ( i != 0 )
2596+ {
2597+ builder . Append ( ", " ) ;
2598+ }
2599+
2600+ builder . Append ( parameterValueVarNames [ i ] ) ;
2601+ }
2602+
2603+ if ( requiredKeywordInitMembers . Length != 0 )
2604+ {
2605+ builder . AppendLine ( ")" ) ;
2606+ builder . Append ( indent ) . AppendLine ( "{" ) ;
2607+ for ( var i = 0 ; i < requiredKeywordInitMembers . Length ; i ++ )
2608+ {
2609+ builder . Append ( indent ) . Append ( " " ) . Append ( requiredKeywordInitMembers [ i ] . Symbol . Name ) . AppendLine ( " = default!," ) ;
2610+ }
2611+
2612+ builder . Append ( indent ) . AppendLine ( "};" ) ;
2613+ }
2614+ else
2615+ {
2616+ builder . AppendLine ( ");" ) ;
2617+ }
2618+ }
2619+
25842620 private static void EmitReadValue (
25852621 StringBuilder builder ,
25862622 int index ,
@@ -5662,7 +5698,8 @@ public MemberModel(
56625698 string ? attributeConverterTypeName ,
56635699 string ? objectCreationHandling ,
56645700 bool isRequired ,
5665- bool isInitOnly )
5701+ bool isInitOnly ,
5702+ bool isRequiredKeyword )
56665703 {
56675704 Symbol = symbol ;
56685705 Type = type ;
@@ -5675,6 +5712,7 @@ public MemberModel(
56755712 ObjectCreationHandling = objectCreationHandling ;
56765713 IsRequired = isRequired ;
56775714 IsInitOnly = isInitOnly ;
5715+ IsRequiredKeyword = isRequiredKeyword ;
56785716 }
56795717
56805718 public ISymbol Symbol { get ; }
@@ -5688,6 +5726,8 @@ public MemberModel(
56885726 public string ? ObjectCreationHandling { get ; }
56895727 public bool IsRequired { get ; }
56905728 public bool IsInitOnly { get ; }
5729+ public bool IsRequiredKeyword { get ; }
5730+ public bool NeedsObjectInitializer => IsInitOnly || IsRequiredKeyword ;
56915731 }
56925732
56935733 private enum ExtensionDataKind
@@ -5739,9 +5779,10 @@ private static MemberModel CreateMemberModel(ISymbol member, JsonNamingPolicy? p
57395779 var ignoreConditionExpression = GetIgnoreConditionExpression ( member ) ;
57405780 var converterTypeName = GetYamlConverterAttributeTypeName ( member ) ;
57415781 var objectCreationHandling = GetObjectCreationHandling ( member ) ;
5742- var isRequired = HasAttribute ( member , "SharpYaml.Serialization.YamlRequiredAttribute" ) || HasAttribute ( member , "System.Text.Json.Serialization.JsonRequiredAttribute" ) ;
5782+ var isRequiredKeyword = member is IPropertySymbol { IsRequired : true } || member is IFieldSymbol { IsRequired : true } ;
5783+ var isRequired = isRequiredKeyword || HasAttribute ( member , "SharpYaml.Serialization.YamlRequiredAttribute" ) || HasAttribute ( member , "System.Text.Json.Serialization.JsonRequiredAttribute" ) ;
57435784 var isInitOnly = member is IPropertySymbol property && IsInitOnlyProperty ( property ) ;
5744- return new MemberModel ( member , type , nameForRead , nameForWrite , accessExpression , assign , ignoreConditionExpression , converterTypeName , objectCreationHandling , isRequired , isInitOnly ) ;
5785+ return new MemberModel ( member , type , nameForRead , nameForWrite , accessExpression , assign , ignoreConditionExpression , converterTypeName , objectCreationHandling , isRequired , isInitOnly , isRequiredKeyword ) ;
57455786 }
57465787
57475788 private static ( string ForRead , string ForWrite ) GetSerializedMemberNameExpressions ( ISymbol member , JsonNamingPolicy ? propertyNamingPolicy )
0 commit comments