Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ComHelpers class to be non-partially declared by compilation #1114

Merged
merged 1 commit into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.Windows.CsWin32/Generator.Handle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public partial class Generator
return safeHandleType;
}

if (this.FindTypeSymbolsIfAlreadyAvailable($"{this.Namespace}.{safeHandleType}").Count > 0)
if (this.FindTypeSymbolIfAlreadyAvailable($"{this.Namespace}.{safeHandleType}") is not null)
{
return safeHandleType;
}
Expand Down
40 changes: 31 additions & 9 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -867,12 +867,24 @@ internal void RequestComHelpers(Context context)
{
if (this.IsWin32Sdk)
{
this.RequestInteropType("Windows.Win32.Foundation", "HRESULT", context);
this.volatileCode.GenerateSpecialType("ComHelpers", () => this.volatileCode.AddSpecialType("ComHelpers", this.comHelperClass));
if (!this.IsTypeAlreadyFullyDeclared($"{this.Namespace}.{this.comHelperClass.Identifier.ValueText}"))
{
this.RequestInteropType("Windows.Win32.Foundation", "HRESULT", context);
this.volatileCode.GenerateSpecialType("ComHelpers", () => this.volatileCode.AddSpecialType("ComHelpers", this.comHelperClass));
}

if (this.IsFeatureAvailable(Feature.InterfaceStaticMembers) && !context.AllowMarshaling)
{
this.volatileCode.GenerateSpecialType("IVTable", () => this.volatileCode.AddSpecialType("IVTable", IVTableInterface));
this.volatileCode.GenerateSpecialType("IVTable`2", () => this.volatileCode.AddSpecialType("IVTable`2", IVTableGenericInterface));
if (!this.IsTypeAlreadyFullyDeclared($"{this.Namespace}.{IVTableInterface.Identifier.ValueText}"))
{
this.volatileCode.GenerateSpecialType("IVTable", () => this.volatileCode.AddSpecialType("IVTable", IVTableInterface));
}

if (!this.IsTypeAlreadyFullyDeclared($"{this.Namespace}.{IVTableGenericInterface.Identifier.ValueText}`2"))
{
this.volatileCode.GenerateSpecialType("IVTable`2", () => this.volatileCode.AddSpecialType("IVTable`2", IVTableGenericInterface));
}

if (!this.TryGenerate("IUnknown", default))
{
throw new GenerationFailedException("Unable to generate IUnknown.");
Expand Down Expand Up @@ -1058,10 +1070,7 @@ internal void GetBaseTypeInfo(TypeDefinition typeDef, out StringHandle baseTypeN
string ns = $"{this.Namespace}.{subNamespace}";
fullyQualifiedName = $"{ns}.{specialName}";

// Skip if the compilation already defines this type or can access it from elsewhere.
// But if we have more than one match, the compiler won't be able to resolve our type references.
// In such a case, we'll prefer to just declare our own local symbol.
if (this.FindTypeSymbolsIfAlreadyAvailable(fullyQualifiedName).Count == 1)
if (this.IsTypeAlreadyFullyDeclared(fullyQualifiedName))
{
// The type already exists either in this project or a referenced one.
return null;
Expand Down Expand Up @@ -1183,6 +1192,19 @@ private bool TryGetRenamedMethod(string methodName, [NotNullWhen(true)] out stri
return false;
}

/// <summary>
/// Checks whether a type with the given name is already defined in the compilation
/// such that we must (or should) skip generating it ourselves.
/// </summary>
/// <param name="fullyQualifiedMetadataName">The fully-qualified metadata name of the type.</param>
/// <returns><see langword="true"/> if the type should <em>not</em> be emitted; <see langword="false" /> if the type is not already declared in the compilation.</returns>
/// <remarks>
/// Skip if the compilation already defines this type or can access it from elsewhere.
/// But if we have more than one match, the compiler won't be able to resolve our type references.
/// In such a case, we'll prefer to just declare our own local symbol.
/// </remarks>
private bool IsTypeAlreadyFullyDeclared(string fullyQualifiedMetadataName) => this.FindTypeSymbolsIfAlreadyAvailable(fullyQualifiedMetadataName).Count == 1;

private ISymbol? FindTypeSymbolIfAlreadyAvailable(string fullyQualifiedMetadataName) => this.FindTypeSymbolsIfAlreadyAvailable(fullyQualifiedMetadataName).FirstOrDefault();

private IReadOnlyList<ISymbol> FindTypeSymbolsIfAlreadyAvailable(string fullyQualifiedMetadataName)
Expand Down Expand Up @@ -1264,7 +1286,7 @@ private IReadOnlyList<ISymbol> FindTypeSymbolsIfAlreadyAvailable(string fullyQua
// Skip if the compilation already defines this type or can access it from elsewhere.
// But if we have more than one match, the compiler won't be able to resolve our type references.
// In such a case, we'll prefer to just declare our own local symbol.
if (this.FindTypeSymbolsIfAlreadyAvailable(fullyQualifiedName).Count == 1)
if (this.IsTypeAlreadyFullyDeclared(fullyQualifiedName))
{
// The type already exists either in this project or a referenced one.
return null;
Expand Down