Skip to content

Commit

Permalink
Merge pull request #139 from microsoft/fix133
Browse files Browse the repository at this point in the history
Generate partials when the project already defines partials
  • Loading branch information
AArnott authored Feb 23, 2021
2 parents 9e09d01 + c1e002e commit 7b56e08
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
5 changes: 4 additions & 1 deletion src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,7 +1582,10 @@ private bool IsCompilerGenerated(TypeDefinition typeDef)
if (this.compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName) is { } ownSymbol)
{
// This assembly defines it.
return ownSymbol;
// But if it defines it as a partial, we should not consider it as fully defined so we populate our side.
return ownSymbol.DeclaringSyntaxReferences.Any(sr => sr.GetSyntax() is BaseTypeDeclarationSyntax type && type.Modifiers.Any(SyntaxKind.PartialKeyword))
? null
: ownSymbol;
}

foreach (var reference in this.compilation.References)
Expand Down
37 changes: 30 additions & 7 deletions test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public void NamespaceHandleGetsNoSafeHandle()
Assert.True(this.generator.TryGenerate("CreatePrivateNamespace", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
Assert.Null(this.FindGeneratedType("ClosePrivateNamespaceSafeHandle"));
Assert.Empty(this.FindGeneratedType("ClosePrivateNamespaceSafeHandle"));
}

[Fact]
Expand Down Expand Up @@ -215,9 +215,8 @@ public void BOOL_FieldRemainsBOOL()
Assert.True(this.generator.TryGenerate("ICONINFO", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
var theStruct = (StructDeclarationSyntax?)this.FindGeneratedType("ICONINFO");
Assert.NotNull(theStruct);
Assert.Equal("BOOL", theStruct!.Members.OfType<FieldDeclarationSyntax>().Select(m => m.Declaration).Single(d => d.Variables.Any(v => v.Identifier.ValueText == "fIcon")).Type.ToString());
var theStruct = (StructDeclarationSyntax)this.FindGeneratedType("ICONINFO").Single();
Assert.Equal("BOOL", theStruct.Members.OfType<FieldDeclarationSyntax>().Select(m => m.Declaration).Single(d => d.Variables.Any(v => v.Identifier.ValueText == "fIcon")).Type.ToString());
}

[Fact]
Expand All @@ -227,7 +226,7 @@ public void BSTR_FieldsDoNotBecomeSafeHandles()
Assert.True(this.generator.TryGenerate("DebugPropertyInfo", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
StructDeclarationSyntax structDecl = Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType("DebugPropertyInfo"));
StructDeclarationSyntax structDecl = Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType("DebugPropertyInfo").Single());
var bstrField = structDecl.Members.OfType<FieldDeclarationSyntax>().First(m => m.Declaration.Variables.Any(v => v.Identifier.ValueText == "m_bstrName"));
Assert.Equal("BSTR", ((IdentifierNameSyntax)bstrField.Declaration.Type).Identifier.ValueText);
}
Expand Down Expand Up @@ -284,10 +283,34 @@ public void StructsArePartial(string structName)
Assert.True(this.generator.TryGenerate(structName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
StructDeclarationSyntax structDecl = Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType(structName));
StructDeclarationSyntax structDecl = Assert.IsType<StructDeclarationSyntax>(this.FindGeneratedType(structName).Single());
Assert.True(structDecl.Modifiers.Any(SyntaxKind.PartialKeyword));
}

[Fact]
public void PartialStructsAllowUserContributions()
{
const string structName = "HRESULT";
this.compilation = this.compilation.AddSyntaxTrees(
CSharpSyntaxTree.ParseText("namespace Microsoft.Windows.Sdk { partial struct HRESULT { void Foo() { } } }", this.parseOptions, "myHRESULT.cs"));

this.generator = new Generator(this.metadataStream, compilation: this.compilation, parseOptions: this.parseOptions);
Assert.True(this.generator.TryGenerate(structName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();

bool hasFooMethod = false;
bool hasValueProperty = false;
foreach (StructDeclarationSyntax structDecl in this.FindGeneratedType(structName))
{
hasFooMethod |= structDecl.Members.OfType<MethodDeclarationSyntax>().Any(m => m.Identifier.ValueText == "Foo");
hasValueProperty |= structDecl.Members.OfType<FieldDeclarationSyntax>().Any(p => p.Declaration.Variables.FirstOrDefault()?.Identifier.ValueText == "Value");
}

Assert.True(hasFooMethod, "User-defined method not found.");
Assert.True(hasValueProperty, "Projected members not found.");
}

[Fact]
public void GetLastErrorGenerationThrowsWhenExplicitlyCalled()
{
Expand Down Expand Up @@ -389,7 +412,7 @@ private CSharpCompilation AddGeneratedCode(CSharpCompilation compilation, Genera

private IEnumerable<MethodDeclarationSyntax> FindGeneratedMethod(string name) => this.compilation.SyntaxTrees.SelectMany(st => st.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>()).Where(md => md.Identifier.ValueText == name);

private BaseTypeDeclarationSyntax? FindGeneratedType(string name) => this.compilation.SyntaxTrees.SelectMany(st => st.GetRoot().DescendantNodes().OfType<BaseTypeDeclarationSyntax>()).FirstOrDefault(md => md.Identifier.ValueText == name);
private IEnumerable<BaseTypeDeclarationSyntax> FindGeneratedType(string name) => this.compilation.SyntaxTrees.SelectMany(st => st.GetRoot().DescendantNodes().OfType<BaseTypeDeclarationSyntax>()).Where(md => md.Identifier.ValueText == name);

private bool IsMethodGenerated(string name) => this.FindGeneratedMethod(name).Any();

Expand Down

0 comments on commit 7b56e08

Please sign in to comment.