Skip to content

Commit

Permalink
Merge pull request #1019 from microsoft/fix988
Browse files Browse the repository at this point in the history
Fix constant wildcard ambiguity warnings
  • Loading branch information
AArnott committed Aug 11, 2023
2 parents 093ccb6 + a3b5015 commit ded8e9c
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 34 deletions.
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

0 comments on commit ded8e9c

Please sign in to comment.