Skip to content

Commit

Permalink
Merge pull request #803 from microsoft/detectFeaturesWithRuntimeFeatures
Browse files Browse the repository at this point in the history
Determine language features support the same way the compiler does
  • Loading branch information
AArnott committed Nov 23, 2022
2 parents 07fe12c + cee5535 commit bde1462
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 12 deletions.
26 changes: 14 additions & 12 deletions src/Microsoft.Windows.CsWin32/Generator.Features.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ public partial class Generator
private readonly bool canUseUnsafeAsRef;
private readonly bool canUseUnsafeNullRef;
private readonly bool unscopedRefAttributePredefined;
private readonly INamedTypeSymbol? runtimeFeatureClass;
private readonly bool generateSupportedOSPlatformAttributes;
private readonly bool generateSupportedOSPlatformAttributesOnInterfaces; // only supported on net6.0 (https://github.com/dotnet/runtime/pull/48838)
private readonly bool generateDefaultDllImportSearchPathsAttribute;
private readonly Dictionary<Feature, bool> supportedFeatures = new();

private void DeclareUnscopedRefAttributeIfNecessary()
{
Expand Down Expand Up @@ -48,21 +50,21 @@ private void DeclareUnscopedRefAttributeIfNecessary()

private bool IsFeatureAvailable(Feature feature)
{
return feature switch
if (this.supportedFeatures.TryGetValue(feature, out bool result))
{
Feature.InterfaceStaticMembers => (int)this.LanguageVersion >= 1100 && this.IsTargetFrameworkAtLeastDotNetVersion(7),
return result;
}

// A feature requires a member on the class, and we ignore features that have the `[RequiresPreviewFeatures]` attribute on them.
bool IsRuntimeFeatureSupported(string name) => this.runtimeFeatureClass?.GetMembers(name).FirstOrDefault()?.GetAttributes().IsEmpty is true;

result = feature switch
{
Feature.InterfaceStaticMembers => (int)this.LanguageVersion >= 1100 && IsRuntimeFeatureSupported("VirtualStaticsInInterfaces"),
_ => throw new NotImplementedException(),
};
}

private bool TryGetTargetDotNetVersion([NotNullWhen(true)] out Version? dotNetVersion)
{
dotNetVersion = this.compilation?.ReferencedAssemblyNames.FirstOrDefault(id => string.Equals(id.Name, "System.Runtime", StringComparison.OrdinalIgnoreCase))?.Version;
return dotNetVersion is not null;
}

private bool IsTargetFrameworkAtLeastDotNetVersion(int majorVersion)
{
return this.TryGetTargetDotNetVersion(out Version? actualVersion) && actualVersion.Major >= majorVersion;
this.supportedFeatures.Add(feature, result);
return result;
}
}
1 change: 1 addition & 0 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public Generator(string metadataLibraryPath, Docs? docs, GeneratorOptions option
this.canUseUnsafeAsRef = this.compilation?.GetTypeByMetadataName(typeof(Unsafe).FullName)?.GetMembers("AsRef").Any() is true;
this.canUseUnsafeNullRef = this.compilation?.GetTypeByMetadataName(typeof(Unsafe).FullName)?.GetMembers("NullRef").Any() is true;
this.unscopedRefAttributePredefined = this.FindTypeSymbolIfAlreadyAvailable("System.Diagnostics.CodeAnalysis.UnscopedRefAttribute") is not null;
this.runtimeFeatureClass = (INamedTypeSymbol?)this.FindTypeSymbolIfAlreadyAvailable("System.Runtime.CompilerServices.RuntimeFeature");
this.comIIDInterfacePredefined = this.FindTypeSymbolIfAlreadyAvailable($"{this.Namespace}.{IComIIDGuidInterfaceName}") is not null;
this.getDelegateForFunctionPointerGenericExists = this.compilation?.GetTypeByMetadataName(typeof(Marshal).FullName)?.GetMembers(nameof(Marshal.GetDelegateForFunctionPointer)).Any(m => m is IMethodSymbol { IsGenericMethod: true }) is true;
this.generateDefaultDllImportSearchPathsAttribute = this.compilation?.GetTypeByMetadataName(typeof(DefaultDllImportSearchPathsAttribute).FullName) is object;
Expand Down

0 comments on commit bde1462

Please sign in to comment.