Skip to content
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
6 changes: 5 additions & 1 deletion src/Microsoft.Windows.CsWin32/Generator.Com.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,13 +1014,17 @@ static ExpressionSyntax ThisPointer(PointerTypeSyntax? typedPointer = null)
propertyOrMethod = methodDeclaration;
}

string apiDocsKey = $"{ifaceName}.{methodName}";
if (inheritedMethods >= 0)
{
propertyOrMethod = propertyOrMethod.AddModifiers(TokenWithSpace(SyntaxKind.NewKeyword));
TypeDefinition declaringType = methodDefinition.Reader.GetTypeDefinition(methodDefinition.Method.GetDeclaringType());
string declaringIfaceName = methodDefinition.Reader.GetString(declaringType.Name);
apiDocsKey = $"{declaringIfaceName}.{methodName}";
}

// Add documentation if we can find it.
propertyOrMethod = this.AddApiDocumentation($"{ifaceName}.{methodName}", propertyOrMethod);
propertyOrMethod = this.AddApiDocumentation(apiDocsKey, propertyOrMethod);

// In source-generator mode, GeneratedComInterface handles inheritance automatically, so inherited
// methods are NOT re-declared on the derived interface. We still need to emit the friendly overloads
Expand Down
29 changes: 29 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/COMTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ public void IDispatchInterfaceIsDual(string ifaceName)
Assert.Equal(nameof(ComInterfaceType.InterfaceIsDual), arg.Name.Identifier.ValueText);
}

[Fact]
public void InheritedCOMInterfaceMethodsUseBaseMethodDocs()
{
this.generator = this.CreateGenerator(includeDocs: true);
this.GenerateApi("IShellDispatch2");

InterfaceDeclarationSyntax baseInterface = Assert.Single(this.FindGeneratedType("IShellDispatch").OfType<InterfaceDeclarationSyntax>());
MethodDeclarationSyntax baseFileRun = Assert.Single(baseInterface.Members.OfType<MethodDeclarationSyntax>(), m => m.Identifier.ValueText == "FileRun");

InterfaceDeclarationSyntax derivedInterface = Assert.Single(this.FindGeneratedType("IShellDispatch2").OfType<InterfaceDeclarationSyntax>());
MethodDeclarationSyntax derivedFileRun = Assert.Single(derivedInterface.Members.OfType<MethodDeclarationSyntax>(), m => m.Identifier.ValueText == "FileRun");
MethodDeclarationSyntax derivedIsRestricted = Assert.Single(derivedInterface.Members.OfType<MethodDeclarationSyntax>(), m => m.Identifier.ValueText == "IsRestricted");

Assert.Equal("void", derivedFileRun.ReturnType.ToString());
Assert.Empty(derivedFileRun.ParameterList.Parameters);
Assert.Contains(derivedFileRun.Modifiers, m => m.IsKind(SyntaxKind.NewKeyword));
string? baseFileRunDocs = GetDocumentationComment(baseFileRun);
Assert.NotNull(baseFileRunDocs);
Assert.Equal(baseFileRunDocs, GetDocumentationComment(derivedFileRun));
Assert.NotNull(GetDocumentationComment(derivedIsRestricted));
}

[Fact]
public void CreateDispatcherQueueController_CreatesWinRTCustomMarshaler()
{
Expand Down Expand Up @@ -1073,4 +1095,11 @@ public void COMStructWrappers_ClsPragmaSuppressionsAreLoadBearing_Issue1703()

private static string StripWarningDisablePragmas(string code) =>
string.Join("\n", code.Split('\n').Where(line => !line.TrimStart().StartsWith("#pragma warning disable", StringComparison.Ordinal)));

private static string? GetDocumentationComment(MemberDeclarationSyntax member) =>
member.GetLeadingTrivia()
.Select(t => t.GetStructure())
.OfType<DocumentationCommentTriviaSyntax>()
.SingleOrDefault()
?.ToFullString();
}
Loading