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

Fix constant wildcard ambiguity warnings #1019

Merged
merged 1 commit into from
Aug 11, 2023
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.Constant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ where field.Key.StartsWith(prefix, StringComparison.Ordinal)
/// <param name="possiblyQualifiedName">The name of the constant, optionally qualified with a namespace.</param>
/// <param name="preciseApi">Receives the canonical API names that <paramref name="possiblyQualifiedName"/> matched on.</param>
/// <returns><see langword="true"/> if a match was found and the constant generated; otherwise <see langword="false"/>.</returns>
public bool TryGenerateConstant(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateConstant(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi)
{
if (possiblyQualifiedName is null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Windows.CsWin32/Generator.Extern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public bool TryGenerateAllExternMethods(string moduleName, CancellationToken can
}

/// <inheritdoc/>
public bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi)
{
if (possiblyQualifiedName is null)
{
Expand Down
8 changes: 4 additions & 4 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public void GenerateAll(CancellationToken cancellationToken)
}

/// <inheritdoc/>
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string> preciseApi, CancellationToken cancellationToken)
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyCollection<string> preciseApi, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(apiNameOrModuleWildcard))
{
Expand Down Expand Up @@ -390,7 +390,7 @@ public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string
/// <param name="namespace">The namespace to generate APIs for.</param>
/// <param name="preciseApi">Receives the canonical API names that <paramref name="namespace"/> matched on.</param>
/// <returns><see langword="true"/> if a matching namespace was found; otherwise <see langword="false"/>.</returns>
public bool TryGenerateNamespace(string @namespace, out IReadOnlyList<string> preciseApi)
public bool TryGenerateNamespace(string @namespace, out IReadOnlyCollection<string> preciseApi)
{
if (@namespace is null)
{
Expand Down Expand Up @@ -506,7 +506,7 @@ public void GenerateAllInteropTypes(CancellationToken cancellationToken)
}

/// <inheritdoc/>
public bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi)
{
if (possiblyQualifiedName is null)
{
Expand Down Expand Up @@ -575,7 +575,7 @@ public bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyList<stri
/// <param name="macroName">The name of the macro. Never qualified with a namespace.</param>
/// <param name="preciseApi">Receives the canonical API names that <paramref name="macroName"/> matched on.</param>
/// <returns><see langword="true"/> if a match was found and the macro generated; otherwise <see langword="false"/>.</returns>
public bool TryGenerateMacro(string macroName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateMacro(string macroName, out IReadOnlyCollection<string> preciseApi)
{
if (macroName is null)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.Windows.CsWin32/GeneratorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace Microsoft.Windows.CsWin32;
/// </summary>
public static class GeneratorExtensions
{
/// <inheritdoc cref="IGenerator.TryGenerate(string, out IReadOnlyList{string}, CancellationToken)"/>
/// <inheritdoc cref="IGenerator.TryGenerate(string, out IReadOnlyCollection{string}, CancellationToken)"/>
public static bool TryGenerate(this IGenerator generator, string apiNameOrModuleWildcard, CancellationToken cancellationToken) => generator.TryGenerate(apiNameOrModuleWildcard, out _, cancellationToken);

/// <inheritdoc cref="IGenerator.TryGenerateType(string, out IReadOnlyList{string})"/>
/// <inheritdoc cref="IGenerator.TryGenerateType(string, out IReadOnlyCollection{string})"/>
public static bool TryGenerateType(this IGenerator generator, string possiblyQualifiedName) => generator.TryGenerateType(possiblyQualifiedName, out _);
}
6 changes: 3 additions & 3 deletions src/Microsoft.Windows.CsWin32/IGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IGenerator : IDisposable
/// <param name="preciseApi">Receives the canonical API names that <paramref name="apiNameOrModuleWildcard"/> matched on.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns><see langword="true" /> if any matching APIs were found and generated; <see langword="false"/> otherwise.</returns>
bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string> preciseApi, CancellationToken cancellationToken);
bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyCollection<string> preciseApi, CancellationToken cancellationToken);

/// <summary>
/// Collects the result of code generation.
Expand All @@ -30,7 +30,7 @@ public interface IGenerator : IDisposable
/// <param name="possiblyQualifiedName">The name of the interop type, optionally qualified with a namespace.</param>
/// <param name="preciseApi">Receives the canonical API names that <paramref name="possiblyQualifiedName"/> matched on.</param>
/// <returns><see langword="true"/> if a match was found and the type generated; otherwise <see langword="false"/>.</returns>
bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi);
bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi);

/// <summary>
/// Generates a projection of all extern methods and their supporting types.
Expand All @@ -56,7 +56,7 @@ public interface IGenerator : IDisposable
/// <param name="possiblyQualifiedName">The name of the extern method, optionally qualified with a namespace.</param>
/// <param name="preciseApi">Receives the canonical API names that <paramref name="possiblyQualifiedName"/> matched on.</param>
/// <returns><see langword="true"/> if a match was found and the extern method generated; otherwise <see langword="false"/>.</returns>
bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi);
bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi);

/// <summary>
/// Generates a projection of all macros.
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.Windows.CsWin32/PrimitiveTypeHandleInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
// Even if the lengths match, if the enum's underlying base type conflicts with the primitive's signed type (e.g. int != uint),
// certain CPU architectures can fail as well.
// So we just have to use the primitive type when marshaling is not allowed.
if (inputs.AllowMarshaling && inputs.Generator?.FindAssociatedEnum(customAttributes) is NameSyntax enumTypeName && inputs.Generator.TryGenerateType(enumTypeName.ToString(), out IReadOnlyList<string> preciseMatch))
if (inputs.AllowMarshaling && inputs.Generator?.FindAssociatedEnum(customAttributes) is NameSyntax enumTypeName && inputs.Generator.TryGenerateType(enumTypeName.ToString(), out IReadOnlyCollection<string> preciseMatch))
{
// Use the qualified name.
enumTypeName = ParseName(Generator.ReplaceCommonNamespaceWithAlias(inputs.Generator, preciseMatch[0]));
enumTypeName = ParseName(Generator.ReplaceCommonNamespaceWithAlias(inputs.Generator, preciseMatch.First()));
MarshalAsAttribute marshalAs = new(GetUnmanagedType(this.PrimitiveTypeCode));
return new TypeSyntaxAndMarshaling(enumTypeName, marshalAs, nativeArrayInfo: null);
}
Expand Down
11 changes: 6 additions & 5 deletions src/Microsoft.Windows.CsWin32/SourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public void Execute(GeneratorExecutionContext context)
continue;
}

superGenerator.TryGenerate(name, out IReadOnlyList<string> matchingApis, out IReadOnlyList<string> redirectedEnums, context.CancellationToken);
superGenerator.TryGenerate(name, out IReadOnlyCollection<string> matchingApis, out IReadOnlyCollection<string> redirectedEnums, context.CancellationToken);
foreach (string declaringEnum in redirectedEnums)
{
context.ReportDiagnostic(Diagnostic.Create(UseEnumValueDeclaringType, location, declaringEnum));
Expand Down Expand Up @@ -269,18 +269,19 @@ public void Execute(GeneratorExecutionContext context)
context.AddSource(unit.Key, unit.Value.GetText(Encoding.UTF8));
}

string ConcatSuggestions(IReadOnlyList<string> suggestions)
string ConcatSuggestions(IReadOnlyCollection<string> suggestions)
{
var suggestionBuilder = new StringBuilder();
for (int i = 0; i < suggestions.Count; i++)
int i = 0;
foreach (string suggestion in suggestions)
{
if (i > 0)
if (++i > 0)
{
suggestionBuilder.Append(i < suggestions.Count - 1 ? ", " : " or ");
}

suggestionBuilder.Append('"');
suggestionBuilder.Append(suggestions[i]);
suggestionBuilder.Append(suggestion);
suggestionBuilder.Append('"');
}

Expand Down
26 changes: 13 additions & 13 deletions src/Microsoft.Windows.CsWin32/SuperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,28 @@ public int TryGenerateAllExternMethods(string moduleName, CancellationToken canc
}

/// <inheritdoc/>
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string> preciseApi, CancellationToken cancellationToken) => this.TryGenerate(apiNameOrModuleWildcard, out preciseApi, out _, cancellationToken);
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyCollection<string> preciseApi, CancellationToken cancellationToken) => this.TryGenerate(apiNameOrModuleWildcard, out preciseApi, out _, cancellationToken);

/// <inheritdoc cref="TryGenerate(string, out IReadOnlyList{string}, CancellationToken)"/>
/// <param name="apiNameOrModuleWildcard"><inheritdoc cref="TryGenerate(string, out IReadOnlyList{string}, CancellationToken)" path="/param[@name='apiNameOrModuleWildcard']"/></param>
/// <param name="preciseApi"><inheritdoc cref="TryGenerate(string, out IReadOnlyList{string}, CancellationToken)" path="/param[@name='preciseApi']"/></param>
/// <inheritdoc cref="TryGenerate(string, out IReadOnlyCollection{string}, CancellationToken)"/>
/// <param name="apiNameOrModuleWildcard"><inheritdoc cref="TryGenerate(string, out IReadOnlyCollection{string}, CancellationToken)" path="/param[@name='apiNameOrModuleWildcard']"/></param>
/// <param name="preciseApi"><inheritdoc cref="TryGenerate(string, out IReadOnlyCollection{string}, CancellationToken)" path="/param[@name='preciseApi']"/></param>
/// <param name="redirectedEnums">Receives names of the enum that declares <paramref name="apiNameOrModuleWildcard"/> as an enum value.</param>
/// <param name="cancellationToken"><inheritdoc cref="TryGenerate(string, out IReadOnlyList{string}, CancellationToken)" path="/param[@name='cancellationToken']"/></param>
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string> preciseApi, out IReadOnlyList<string> redirectedEnums, CancellationToken cancellationToken)
/// <param name="cancellationToken"><inheritdoc cref="TryGenerate(string, out IReadOnlyCollection{string}, CancellationToken)" path="/param[@name='cancellationToken']"/></param>
public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyCollection<string> preciseApi, out IReadOnlyCollection<string> redirectedEnums, CancellationToken cancellationToken)
{
List<string> preciseApiAccumulator = new();
List<string> redirectedEnumsAccumulator = new();
HashSet<string> preciseApiAccumulator = new(StringComparer.Ordinal);
HashSet<string> redirectedEnumsAccumulator = new(StringComparer.Ordinal);
bool success = false;
foreach (Generator generator in this.Generators.Values)
{
if (generator.TryGenerate(apiNameOrModuleWildcard, out preciseApi, cancellationToken))
{
preciseApiAccumulator.AddRange(preciseApi);
preciseApiAccumulator.UnionWith(preciseApi);
success = true;
continue;
}

preciseApiAccumulator.AddRange(preciseApi);
preciseApiAccumulator.UnionWith(preciseApi);
if (generator.TryGetEnumName(apiNameOrModuleWildcard, out string? declaringEnum))
{
redirectedEnumsAccumulator.Add(declaringEnum);
Expand All @@ -89,7 +89,7 @@ public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string
success = true;
}

preciseApiAccumulator.AddRange(preciseApi);
preciseApiAccumulator.UnionWith(preciseApi);
}
}

Expand All @@ -99,7 +99,7 @@ public bool TryGenerate(string apiNameOrModuleWildcard, out IReadOnlyList<string
}

/// <inheritdoc/>
public bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateType(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi)
{
List<string> preciseApiAccumulator = new();
bool success = false;
Expand Down Expand Up @@ -141,7 +141,7 @@ public void GenerateAllInteropTypes(CancellationToken cancellationToken)
}

/// <inheritdoc/>
public bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyList<string> preciseApi)
public bool TryGenerateExternMethod(string possiblyQualifiedName, out IReadOnlyCollection<string> preciseApi)
{
List<string> preciseApiAccumulator = new();
bool success = false;
Expand Down
21 changes: 19 additions & 2 deletions test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,28 @@ public void WildcardForConstants(bool withNamespace)
Assert.Empty(this.FindGeneratedConstant("ALG_SID_HMAC"));
}

/// <summary>
/// Asserts that the source generator will not emit a warning when a wildcard is used to generate constants that match in more than one metadata assembly.
/// </summary>
[Fact]
public void WildcardForConstants_DefinedAcrossMetadata()
{
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate("IOCTL_*", out IReadOnlyCollection<string> preciseApi, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
Assert.Single(this.FindGeneratedConstant("IOCTL_ABORT_PIPE")); // Win32
Assert.Single(this.FindGeneratedConstant("IOCTL_REDIR_QUERY_PATH")); // WDK

// If this produces more than one result, the source generator will complain.
Assert.Single(preciseApi);
}

[Fact]
public void WildcardForConstants_NoMatch()
{
this.generator = this.CreateGenerator();
Assert.False(this.generator.TryGenerate("IDONTEXIST*", out IReadOnlyList<string> preciseApi, CancellationToken.None));
Assert.False(this.generator.TryGenerate("IDONTEXIST*", out IReadOnlyCollection<string> preciseApi, CancellationToken.None));
Assert.Empty(preciseApi);
}

Expand Down Expand Up @@ -369,7 +386,7 @@ public void Decimal([CombinatorialValues("net472", "net6.0")] string tfm)
public void AmbiguousApiName()
{
this.generator = this.CreateGenerator();
Assert.False(this.generator.TryGenerate("IDENTITY_TYPE", out IReadOnlyList<string> preciseApi, CancellationToken.None));
Assert.False(this.generator.TryGenerate("IDENTITY_TYPE", out IReadOnlyCollection<string> preciseApi, CancellationToken.None));
Assert.Equal(2, preciseApi.Count);
Assert.Contains("Windows.Win32.NetworkManagement.NetworkPolicyServer.IDENTITY_TYPE", preciseApi);
Assert.Contains("Windows.Win32.Security.Authentication.Identity.Provider.IDENTITY_TYPE", preciseApi);
Expand Down
2 changes: 1 addition & 1 deletion test/Microsoft.Windows.CsWin32.Tests/StructTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public void FieldWithAssociatedEnum()
public void SpecialStruct_ByRequest(string structName)
{
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(structName, out IReadOnlyList<string> preciseApi, CancellationToken.None));
Assert.True(this.generator.TryGenerate(structName, out IReadOnlyCollection<string> preciseApi, CancellationToken.None));
Assert.Single(preciseApi);
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
Expand Down