Skip to content

Commit

Permalink
Adds support for namespace XML doc comments
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed Nov 23, 2016
1 parent 564afd3 commit 5fee98c
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 51 deletions.
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

0 comments on commit 5fee98c

Please sign in to comment.