Skip to content

Commit f4f4d8d

Browse files
authored
Fix Validation SG for non-Web SDK projects (#63687)
1 parent bb84476 commit f4f4d8d

File tree

4 files changed

+55
-13
lines changed

4 files changed

+55
-13
lines changed

src/Shared/RoslynUtils/WellKnownTypes.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,28 @@ public INamedTypeSymbol Get(SpecialType type)
5858
return _compilation.GetSpecialType(type);
5959
}
6060

61+
/// <summary>
62+
/// Returns the type symbol for the specified well-known type, or throws if the type cannot be found.
63+
/// </summary>
6164
public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
65+
{
66+
return Get(type, throwOnNotFound: true);
67+
}
68+
69+
/// <summary>
70+
/// Returns the type symbol for the specified well-known type, or a special marker type symbol if the type cannot be found.
71+
/// </summary>
72+
/// <remarks>
73+
/// We use a special marker type for cases where some types can be legitimately missing.
74+
/// E.g. The Microsoft.Extensions.Validation source generator checks against some types
75+
/// from the shared framework which are missing in Blazor WebAssembly SDK projects.
76+
/// </remarks>
77+
public INamedTypeSymbol GetOptional(WellKnownTypeData.WellKnownType type)
78+
{
79+
return Get(type, throwOnNotFound: false);
80+
}
81+
82+
private INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type, bool throwOnNotFound)
6283
{
6384
var index = (int)type;
6485
var symbol = _lazyWellKnownTypes[index];
@@ -69,16 +90,22 @@ public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
6990

7091
// Symbol hasn't been added to the cache yet.
7192
// Resolve symbol from name, cache, and return.
72-
return GetAndCache(index);
93+
return GetAndCache(index, throwOnNotFound);
7394
}
7495

75-
private INamedTypeSymbol GetAndCache(int index)
96+
private INamedTypeSymbol GetAndCache(int index, bool throwOnNotFound)
7697
{
7798
var result = GetTypeByMetadataNameInTargetAssembly(WellKnownTypeData.WellKnownTypeNames[index]);
78-
if (result == null)
99+
100+
if (result == null && throwOnNotFound)
79101
{
80102
throw new InvalidOperationException($"Failed to resolve well-known type '{WellKnownTypeData.WellKnownTypeNames[index]}'.");
81103
}
104+
else
105+
{
106+
result ??= _compilation.GetTypeByMetadataName(typeof(MissingType).FullName!)!;
107+
}
108+
82109
Interlocked.CompareExchange(ref _lazyWellKnownTypes[index], result, null);
83110

84111
// GetTypeByMetadataName should always return the same instance for a name.
@@ -159,4 +186,6 @@ public static bool Implements(ITypeSymbol? type, ITypeSymbol interfaceType)
159186
}
160187
return false;
161188
}
189+
190+
internal class MissingType { }
162191
}

src/Validation/gen/Extensions/ITypeSymbolExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ internal static bool ImplementsInterface(this ITypeSymbol type, ITypeSymbol inte
9595
// types themselves so we short-circuit on them.
9696
internal static bool IsExemptType(this ITypeSymbol type, WellKnownTypes wellKnownTypes)
9797
{
98-
return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
99-
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
100-
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
98+
return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
99+
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
100+
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
101101
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Threading_CancellationToken))
102-
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
103-
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
104-
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
102+
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
103+
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
104+
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
105105
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Stream))
106106
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Pipelines_PipeReader));
107107
}

src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ internal ImmutableArray<ValidatableType> ExtractValidatableTypes(IInvocationOper
2727
? method.Parameters
2828
: [];
2929

30-
var fromServiceMetadataSymbol = wellKnownTypes.Get(
30+
var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
3131
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
32-
var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
32+
var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
3333
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
3434
var skipValidationAttributeSymbol = wellKnownTypes.Get(
3535
WellKnownTypeData.WellKnownType.Microsoft_Extensions_Validation_SkipValidationAttribute);
@@ -127,9 +127,9 @@ internal ImmutableArray<ValidatableProperty> ExtractValidatableMembers(ITypeSymb
127127
var members = new List<ValidatableProperty>();
128128
var resolvedRecordProperty = new List<IPropertySymbol>();
129129

130-
var fromServiceMetadataSymbol = wellKnownTypes.Get(
130+
var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
131131
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
132-
var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
132+
var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
133133
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
134134
var jsonIgnoreAttributeSymbol = wellKnownTypes.Get(
135135
WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonIgnoreAttribute);

src/Validation/src/Microsoft.Extensions.Validation.csproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
<EnableDefaultItems>true</EnableDefaultItems>
1212
</PropertyGroup>
1313

14+
<ItemGroup>
15+
<ProjectReference Include="$(RepoRoot)src\Validation\gen\Microsoft.Extensions.Validation.ValidationsGenerator.csproj" Pack="false">
16+
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
17+
<OutputItemType>Content</OutputItemType>
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
</ProjectReference>
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<!-- Package the generator in the analyzer directory of the nuget package -->
24+
<None Include="$(OutputPath)\$(AssemblyName).ValidationsGenerator.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
25+
</ItemGroup>
26+
1427
<ItemGroup>
1528
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
1629
<Reference Include="Microsoft.Extensions.Options" />

0 commit comments

Comments
 (0)