diff --git a/src/Shared/RoslynUtils/WellKnownTypes.cs b/src/Shared/RoslynUtils/WellKnownTypes.cs
index deb2def60b60..05925ddf68bc 100644
--- a/src/Shared/RoslynUtils/WellKnownTypes.cs
+++ b/src/Shared/RoslynUtils/WellKnownTypes.cs
@@ -19,7 +19,6 @@ public static WellKnownTypes GetOrCreate(Compilation compilation) =>
private readonly INamedTypeSymbol?[] _lazyWellKnownTypes;
private readonly Compilation _compilation;
- private readonly INamedTypeSymbol _missingTypeSymbol;
static WellKnownTypes()
{
@@ -52,7 +51,6 @@ private WellKnownTypes(Compilation compilation)
{
_lazyWellKnownTypes = new INamedTypeSymbol?[WellKnownTypeData.WellKnownTypeNames.Length];
_compilation = compilation;
- _missingTypeSymbol = compilation.GetTypeByMetadataName(typeof(MissingType).FullName!)!;
}
public INamedTypeSymbol Get(SpecialType type)
@@ -60,7 +58,28 @@ public INamedTypeSymbol Get(SpecialType type)
return _compilation.GetSpecialType(type);
}
+ ///
+ /// Returns the type symbol for the specified well-known type, or throws if the type cannot be found.
+ ///
public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
+ {
+ return Get(type, throwOnNotFound: true);
+ }
+
+ ///
+ /// Returns the type symbol for the specified well-known type, or a special marker type symbol if the type cannot be found.
+ ///
+ ///
+ /// We use a special marker type for cases where some types can be legitimately missing.
+ /// E.g. The Microsoft.Extensions.Validation source generator checks against some types
+ /// from the shared framework which are missing in Blazor WebAssembly SDK projects.
+ ///
+ public INamedTypeSymbol GetOptional(WellKnownTypeData.WellKnownType type)
+ {
+ return Get(type, throwOnNotFound: false);
+ }
+
+ private INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type, bool throwOnNotFound)
{
var index = (int)type;
var symbol = _lazyWellKnownTypes[index];
@@ -71,12 +90,22 @@ public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
// Symbol hasn't been added to the cache yet.
// Resolve symbol from name, cache, and return.
- return GetAndCache(index);
+ return GetAndCache(index, throwOnNotFound);
}
- private INamedTypeSymbol GetAndCache(int index)
+ private INamedTypeSymbol GetAndCache(int index, bool throwOnNotFound)
{
- var result = GetTypeByMetadataNameInTargetAssembly(WellKnownTypeData.WellKnownTypeNames[index]) ?? _missingTypeSymbol;
+ var result = GetTypeByMetadataNameInTargetAssembly(WellKnownTypeData.WellKnownTypeNames[index]);
+
+ if (result == null && throwOnNotFound)
+ {
+ throw new InvalidOperationException($"Failed to resolve well-known type '{WellKnownTypeData.WellKnownTypeNames[index]}'.");
+ }
+ else
+ {
+ result ??= _compilation.GetTypeByMetadataName(typeof(MissingType).FullName!)!;
+ }
+
Interlocked.CompareExchange(ref _lazyWellKnownTypes[index], result, null);
// GetTypeByMetadataName should always return the same instance for a name.
diff --git a/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs b/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
index 4b34054571aa..63261c68a85d 100644
--- a/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
+++ b/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
@@ -95,13 +95,13 @@ internal static bool ImplementsInterface(this ITypeSymbol type, ITypeSymbol inte
// types themselves so we short-circuit on them.
internal static bool IsExemptType(this ITypeSymbol type, WellKnownTypes wellKnownTypes)
{
- return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
+ return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Threading_CancellationToken))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Stream))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Pipelines_PipeReader));
}
diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
index cc0624fdfb93..71466a8a547b 100644
--- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
+++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
@@ -27,9 +27,9 @@ internal ImmutableArray ExtractValidatableTypes(IInvocationOper
? method.Parameters
: [];
- var fromServiceMetadataSymbol = wellKnownTypes.Get(
+ var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
- var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
+ var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
var skipValidationAttributeSymbol = wellKnownTypes.Get(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_Validation_SkipValidationAttribute);
@@ -127,9 +127,9 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb
var members = new List();
var resolvedRecordProperty = new List();
- var fromServiceMetadataSymbol = wellKnownTypes.Get(
+ var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
- var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
+ var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
var jsonIgnoreAttributeSymbol = wellKnownTypes.Get(
WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonIgnoreAttribute);