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

Adds support for namespace XML doc comments #15494

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ internal static string GetDocumentationCommentXml(Symbol symbol, bool processInc
symbol.Kind == SymbolKind.Field ||
symbol.Kind == SymbolKind.Method ||
symbol.Kind == SymbolKind.NamedType ||
symbol.Kind == SymbolKind.Property);
symbol.Kind == SymbolKind.Property ||
symbol.Kind == SymbolKind.Namespace);

CSharpCompilation compilation = symbol.DeclaringCompilation;
Debug.Assert(compilation != null);
Expand Down Expand Up @@ -166,40 +167,47 @@ public override void VisitNamespace(NamespaceSymbol symbol)
{
_cancellationToken.ThrowIfCancellationRequested();

if (symbol.IsGlobalNamespace)
if (!_isForSingleSymbol)
{
Debug.Assert(_assemblyName != null);
if (symbol.IsGlobalNamespace)
{
Debug.Assert(_assemblyName != null);

WriteLine("<?xml version=\"1.0\"?>");
WriteLine("<doc>");
Indent();
WriteLine("<?xml version=\"1.0\"?>");
WriteLine("<doc>");
Indent();

if (!_compilation.Options.OutputKind.IsNetModule())
{
WriteLine("<assembly>");
if (!_compilation.Options.OutputKind.IsNetModule())
{
WriteLine("<assembly>");
Indent();
WriteLine("<name>{0}</name>", _assemblyName);
Unindent();
WriteLine("</assembly>");
}

WriteLine("<members>");
Indent();
WriteLine("<name>{0}</name>", _assemblyName);
Unindent();
WriteLine("</assembly>");
}

WriteLine("<members>");
Indent();
}

Debug.Assert(!_isForSingleSymbol);
foreach (var s in symbol.GetMembers())
{
_cancellationToken.ThrowIfCancellationRequested();
s.Accept(this);
}
DefaultVisit(symbol);

if (symbol.IsGlobalNamespace)
if (!_isForSingleSymbol)
{
Unindent();
WriteLine("</members>");
Unindent();
WriteLine("</doc>");
foreach (var s in symbol.GetMembers())
{
_cancellationToken.ThrowIfCancellationRequested();
s.Accept(this);
}

if (symbol.IsGlobalNamespace)
{
Unindent();
WriteLine("</members>");
Unindent();
WriteLine("</doc>");
}
}
}

Expand Down Expand Up @@ -547,12 +555,13 @@ private static ImmutableArray<TypeParameterSymbol> GetTypeParameters(Symbol symb
/// A symbol requires a documentation comment if it was explicitly declared and
/// will be visible outside the current assembly (ignoring InternalsVisibleTo).
/// Exception: accessors do not require doc comments.
/// Exception: namespaces do not require doc comments.
/// </summary>
private static bool RequiresDocumentationComment(Symbol symbol)
{
Debug.Assert((object)symbol != null);

if (symbol.IsImplicitlyDeclared || symbol.IsAccessor())
if (symbol.IsImplicitlyDeclared || symbol.IsAccessor() || symbol.Kind == SymbolKind.Namespace)
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Roslyn.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
Expand Down Expand Up @@ -42,6 +44,8 @@ internal abstract class PENamespaceSymbol
/// </summary>
private ImmutableArray<PENamedTypeSymbol> _lazyFlattenedTypes;

private Tuple<CultureInfo, string> _lazyDocComment;

internal sealed override NamespaceExtent Extent
{
get
Expand Down Expand Up @@ -308,5 +312,10 @@ internal NamedTypeSymbol LookupMetadataType(ref MetadataTypeName emittedTypeName

return result;
}

public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return PEDocumentationCommentUtils.GetDocumentationComment(this, ContainingPEModule, preferredCulture, cancellationToken, ref _lazyDocComment);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis;
using System.Globalization;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
Expand All @@ -26,6 +27,7 @@ internal partial class SourceNamespaceSymbol : NamespaceSymbol
private Dictionary<string, ImmutableArray<NamedTypeSymbol>> _nameToTypeMembersMap;
private ImmutableArray<Symbol> _lazyAllMembers;
private ImmutableArray<NamedTypeSymbol> _lazyTypeMembersUnordered;
private string _lazyDocComment;

private const int LazyAllMembersIsSorted = 0x1; // Set if "lazyAllMembers" is sorted.
private int _flags;
Expand Down Expand Up @@ -547,5 +549,10 @@ public Dictionary<String, ImmutableArray<NamespaceOrTypeSymbol>> CreateMap()
return result;
}
}

public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(this, expandIncludes, ref _lazyDocComment);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3799,6 +3799,34 @@ class C { }
Assert.Equal(expectedText, actualText);
}

[Fact]
public void ForSingleNamespace()
{
var source = @"
/// <summary>
/// A
/// B
/// C
/// </summary>
namespace N { class C { } }
";

var compilation = CreateCompilationWithMscorlibAndDocumentationComments(source);

var type = compilation.GlobalNamespace.GetMember<NamespaceSymbol>("N");
var actualText = DocumentationCommentCompiler.GetDocumentationCommentXml(type, processIncludes: true, cancellationToken: default(CancellationToken));
var expectedText =
@"<member name=""N:N"">
<summary>
A
B
C
</summary>
</member>
";
Assert.Equal(expectedText, actualText);
}

[Fact]
public void ForSingleTypeWithInclude()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic

Case SymbolKind.Event
Return "event"

Case SymbolKind.Namespace
Return "namespace"

Case SymbolKind.NamedType
Select Case DirectCast(symbol, NamedTypeSymbol).TypeKind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,112 @@ Namespace Microsoft.CodeAnalysis.VisualBasic

If Not ShouldSkipSymbol(symbol) Then

If symbol.IsGlobalNamespace Then
Debug.Assert(Me._assemblyName IsNot Nothing)
If Not Me._isForSingleSymbol Then
If symbol.IsGlobalNamespace Then
Debug.Assert(Me._assemblyName IsNot Nothing)

WriteLine("<?xml version=""1.0""?>")
WriteLine("<doc>")
Indent()
WriteLine("<?xml version=""1.0""?>")
WriteLine("<doc>")
Indent()

If Not Me._compilation.Options.OutputKind.IsNetModule() Then
WriteLine("<assembly>")
Indent()
WriteLine("<name>")
WriteLine(Me._assemblyName)
WriteLine("</name>")
Unindent()
WriteLine("</assembly>")
End If

If Not Me._compilation.Options.OutputKind.IsNetModule() Then
WriteLine("<assembly>")
WriteLine("<members>")
Indent()
WriteLine("<name>")
WriteLine(Me._assemblyName)
WriteLine("</name>")
Unindent()
WriteLine("</assembly>")
End If
End If

WriteLine("<members>")
Indent()
WriteDocumentationCommentForNamedType(symbol)

If Not Me._isForSingleSymbol Then
For Each member In symbol.GetMembers()
Me.Visit(member)
Next

If symbol.IsGlobalNamespace Then
Unindent()
WriteLine("</members>")
Unindent()
WriteLine("</doc>")
End If
End If

Debug.Assert(Not Me._isForSingleSymbol, "Do not expect a doc comment query for a single namespace")
For Each member In symbol.GetMembers()
Me.Visit(member)
Next
End If
End Sub

Private Sub WriteDocumentationCommentForNamedType(symbol As Symbols.NamespaceSymbol)

Dim multipleDocComments = ArrayBuilder(Of DocumentationCommentTriviaSyntax).GetInstance
Dim maxDocumentationMode As DocumentationMode = DocumentationMode.None

If symbol.IsGlobalNamespace Then
Unindent()
WriteLine("</members>")
Unindent()
WriteLine("</doc>")
Dim symbolName As String = GetSymbolName(symbol)

If multipleDocComments.Count > 1 Then
' In case we have multiple documentation comments we should discard
' all of them with (optionally) reporting all diagnostics
If maxDocumentationMode = DocumentationMode.Diagnose Then
For Each trivia In multipleDocComments
Me._diagnostics.Add(ERRID.WRN_XMLDocOnAPartialType, trivia.GetLocation(), symbolName)
Next
End If
multipleDocComments.Free()

' No further processing of any of the found documentation comments
Return

ElseIf multipleDocComments.Count = 0 Then
multipleDocComments.Free()
Return
End If

Dim wellKnownElementNodes As New Dictionary(Of WellKnownTag, ArrayBuilder(Of XmlNodeSyntax))
Dim theOnlyDocumentationCommentTrivia As DocumentationCommentTriviaSyntax = multipleDocComments(0)
multipleDocComments.Free()

Dim docCommentXml As String = GetDocumentationCommentForSymbol(symbol, theOnlyDocumentationCommentTrivia, wellKnownElementNodes)

' No further processing
If docCommentXml Is Nothing Then
FreeWellKnownElementNodes(wellKnownElementNodes)
Return
End If

If theOnlyDocumentationCommentTrivia.SyntaxTree.ReportDocumentationCommentDiagnostics OrElse _writer.IsSpecified Then

' Duplicate top-level well known tags
ReportWarningsForDuplicatedTags(wellKnownElementNodes)

' <exception>
ReportIllegalWellKnownTagIfAny(WellKnownTag.Exception, wellKnownElementNodes, symbolName)

' <returns>
ReportIllegalWellKnownTagIfAny(WellKnownTag.Returns, wellKnownElementNodes, symbolName)

' <param> & <paramref>
ReportIllegalWellKnownTagIfAny(WellKnownTag.Param, wellKnownElementNodes, symbolName)
ReportIllegalWellKnownTagIfAny(WellKnownTag.ParamRef, wellKnownElementNodes, symbolName)

' <value>
ReportIllegalWellKnownTagIfAny(WellKnownTag.Value, wellKnownElementNodes, symbolName)

' <typeparam>
ReportIllegalWellKnownTagIfAny(WellKnownTag.TypeParam, wellKnownElementNodes, symbolName)

' <typeparamref>
ReportWarningsForTypeParamRefTags(wellKnownElementNodes, symbolName, symbol, theOnlyDocumentationCommentTrivia.SyntaxTree)
End If

FreeWellKnownElementNodes(wellKnownElementNodes)

WriteDocumentationCommentForSymbol(docCommentXml)
End Sub

End Class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
symbol.Kind = SymbolKind.Field OrElse
symbol.Kind = SymbolKind.Method OrElse
symbol.Kind = SymbolKind.NamedType OrElse
symbol.Kind = SymbolKind.Property)
symbol.Kind = SymbolKind.Property OrElse
symbol.Kind = SymbolKind.Namespace)

Dim compilation As VisualBasicCompilation = symbol.DeclaringCompilation
Debug.Assert(compilation IsNot Nothing)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

Imports System.Collections.Immutable
Imports System.Globalization
Imports System.Threading
Imports Microsoft.CodeAnalysis.ImmutableArrayExtensions
Imports Microsoft.CodeAnalysis.Text
Expand All @@ -17,6 +18,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Private _nameToMembersMap As Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol))
Private _nameToTypeMembersMap As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
Private _lazyEmbeddedKind As Integer = EmbeddedSymbolKind.Unset
Private _lazyDocComment As String

' lazily evaluated state of the symbol (StateFlags)
Private _lazyState As Integer
Expand Down Expand Up @@ -666,5 +668,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Return _declaration
End Get
End Property

Public Overrides Function GetDocumentationCommentXml(Optional preferredCulture As CultureInfo = Nothing, Optional expandIncludes As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As String
If _lazyDocComment Is Nothing Then
' NOTE: replace Nothing with empty comment
Interlocked.CompareExchange(
_lazyDocComment, GetDocumentationCommentForSymbol(Me, preferredCulture, expandIncludes, cancellationToken), Nothing)
End If

Return _lazyDocComment
End Function

End Class
End Namespace