@@ -32,7 +32,8 @@ private record TypeValidatorConfig(
3232 bool DisallowAny ,
3333 SyntaxBase ? OriginSyntax ,
3434 TypeMismatchDiagnosticWriter ? OnTypeMismatch ,
35- bool IsResourceDeclaration ) ;
35+ bool IsResourceDeclaration ,
36+ HashSet < ( SyntaxBase expression , TypeSymbol expressionType , TypeSymbol targetType ) > currentlyProcessing ) ;
3637
3738 private TypeValidator ( ITypeManager typeManager , IBinder binder , IDiagnosticLookup parsingErrorLookup , IDiagnosticWriter diagnosticWriter )
3839 {
@@ -218,7 +219,8 @@ public static TypeSymbol NarrowTypeAndCollectDiagnostics(ITypeManager typeManage
218219 DisallowAny : false ,
219220 OriginSyntax : null ,
220221 OnTypeMismatch : null ,
221- IsResourceDeclaration : isResourceDeclaration ) ;
222+ IsResourceDeclaration : isResourceDeclaration ,
223+ currentlyProcessing : new ( ) ) ;
222224
223225 var validator = new TypeValidator ( typeManager , binder , parsingErrorLookup , diagnosticWriter ) ;
224226
@@ -1139,7 +1141,7 @@ static TypeSymbol RemoveImplicitNull(TypeSymbol type, bool typeWasPreserved)
11391141 diagnosticWriter . Write ( diagnosticTarget , x => x . CannotAssignToReadOnlyProperty ( resourceTypeInaccuracy || ShouldWarnForPropertyMismatch ( targetType ) , declaredProperty . Name , resourceTypeInaccuracy ) ) ;
11401142 }
11411143
1142- narrowedProperties . Add ( new NamedTypeProperty ( declaredProperty . Name , declaredProperty . TypeReference . Type , declaredProperty . Flags ) ) ;
1144+ narrowedProperties . Add ( declaredProperty ) ;
11431145 continue ;
11441146 }
11451147
@@ -1149,25 +1151,40 @@ static TypeSymbol RemoveImplicitNull(TypeSymbol type, bool typeWasPreserved)
11491151 x => x . FallbackPropertyUsed ( shouldDowngrade : false , declaredProperty . Name ) ) ;
11501152 }
11511153
1152- var newConfig = new TypeValidatorConfig (
1153- SkipConstantCheck : skipConstantCheckForProperty ,
1154- SkipTypeErrors : true ,
1155- DisallowAny : declaredProperty . Flags . HasFlag ( TypePropertyFlags . DisallowAny ) ,
1156- OriginSyntax : config . OriginSyntax ,
1157- OnTypeMismatch : GetPropertyMismatchDiagnosticWriter (
1154+ var newConfig = config with
1155+ {
1156+ SkipConstantCheck = skipConstantCheckForProperty ,
1157+ SkipTypeErrors = true ,
1158+ DisallowAny = declaredProperty . Flags . HasFlag ( TypePropertyFlags . DisallowAny ) ,
1159+ OnTypeMismatch = GetPropertyMismatchDiagnosticWriter (
11581160 config : config ,
11591161 shouldWarn : ( config . IsResourceDeclaration && ! declaredProperty . Flags . HasFlag ( TypePropertyFlags . SystemProperty ) ) || ShouldWarn ( declaredProperty . TypeReference . Type ) ,
11601162 propertyName : declaredProperty . Name ,
11611163 showTypeInaccuracyClause : config . IsResourceDeclaration && ! declaredProperty . Flags . HasFlag ( TypePropertyFlags . SystemProperty ) ) ,
1162- IsResourceDeclaration : config . IsResourceDeclaration ) ;
1164+ } ;
1165+
1166+ var propertyExpression = declaredPropertySyntax ? . Value ?? expression ;
1167+ TypeSymbol propertyTargetType = declaredProperty . TypeReference . Type ;
1168+ TypeSymbol propertyExpressionType = expressionTypeProperty . TypeReference . Type ;
1169+
1170+ TypeSymbol GetNarrowedPropertyType ( )
1171+ {
1172+ // append "| null" to the property type for non-required properties
1173+ var ( propertyAssignmentType , typeWasPreserved ) = AddImplicitNull ( propertyTargetType , declaredProperty . Flags ) ;
11631174
1164- // append "| null" to the property type for non-required properties
1165- var ( propertyAssignmentType , typeWasPreserved ) = AddImplicitNull ( declaredProperty . TypeReference . Type , declaredProperty . Flags ) ;
1175+ var narrowedType = NarrowType ( newConfig , propertyExpression , propertyExpressionType , propertyAssignmentType ) ;
1176+ return RemoveImplicitNull ( narrowedType , typeWasPreserved ) ;
1177+ }
11661178
1167- var narrowedType = NarrowType ( newConfig , declaredPropertySyntax ? . Value ?? expression , expressionTypeProperty . TypeReference . Type , propertyAssignmentType ) ;
1168- narrowedType = RemoveImplicitNull ( narrowedType , typeWasPreserved ) ;
1179+ // In the case of a recursive type, eager narrowing can lead to infinite recursion. If we've
1180+ // already narrowed this (expressionSyntax, expressionType, targetType) triple, then all
1181+ // relevant diagnostics have already been raised. Use a deferred type reference to stop eagerly
1182+ // comparing and narrowing types from this point forward.
1183+ ITypeReference narrowedPropertyType = config . currentlyProcessing . Add ( ( propertyExpression , propertyExpressionType , propertyTargetType ) )
1184+ ? GetNarrowedPropertyType ( )
1185+ : new DeferredTypeReference ( GetNarrowedPropertyType ) ;
11691186
1170- narrowedProperties . Add ( new NamedTypeProperty ( declaredProperty . Name , narrowedType , declaredProperty . Flags ) ) ;
1187+ narrowedProperties . Add ( new NamedTypeProperty ( declaredProperty . Name , narrowedPropertyType , declaredProperty . Flags ) ) ;
11711188 }
11721189 else
11731190 {
@@ -1236,19 +1253,22 @@ static TypeSymbol RemoveImplicitNull(TypeSymbol type, bool typeWasPreserved)
12361253 skipConstantCheckForProperty = true ;
12371254 }
12381255
1239- var newConfig = new TypeValidatorConfig (
1240- SkipConstantCheck : skipConstantCheckForProperty ,
1241- SkipTypeErrors : true ,
1242- DisallowAny : targetType . AdditionalProperties . Flags . HasFlag ( TypePropertyFlags . DisallowAny ) ,
1243- OriginSyntax : config . OriginSyntax ,
1244- OnTypeMismatch : GetPropertyMismatchDiagnosticWriter ( config , ShouldWarn ( targetType . AdditionalProperties . TypeReference . Type ) , extraProperty . Key , false ) ,
1245- IsResourceDeclaration : config . IsResourceDeclaration ) ;
1256+ var newConfig = config with
1257+ {
1258+ SkipConstantCheck = skipConstantCheckForProperty ,
1259+ SkipTypeErrors = true ,
1260+ DisallowAny = targetType . AdditionalProperties . Flags . HasFlag ( TypePropertyFlags . DisallowAny ) ,
1261+ OnTypeMismatch = GetPropertyMismatchDiagnosticWriter ( config , ShouldWarn ( targetType . AdditionalProperties . TypeReference . Type ) , extraProperty . Key , false ) ,
1262+ } ;
12461263
12471264 // append "| null" to the type on non-required properties
12481265 var ( additionalPropertiesAssignmentType , _) = AddImplicitNull ( targetType . AdditionalProperties . TypeReference . Type , targetType . AdditionalProperties . Flags ) ;
12491266
12501267 // although we don't use the result here, it's important to call NarrowType to collect diagnostics
1251- var narrowedType = NarrowType ( newConfig , extraPropertySyntax ? . Value ?? expression , extraProperty . Value . TypeReference . Type , additionalPropertiesAssignmentType ) ;
1268+ if ( config . currentlyProcessing . Add ( ( extraPropertySyntax ? . Value ?? expression , extraProperty . Value . TypeReference . Type , additionalPropertiesAssignmentType ) ) )
1269+ {
1270+ var narrowedType = NarrowType ( newConfig , extraPropertySyntax ? . Value ?? expression , extraProperty . Value . TypeReference . Type , additionalPropertiesAssignmentType ) ;
1271+ }
12521272
12531273 // TODO should we try and narrow the additional properties type? May be difficult
12541274 }
0 commit comments