| File: DocumentationComments\SourceDocumentationCommentUtils.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp { internal static class SourceDocumentationCommentUtils { internal static string GetAndCacheDocumentationComment(Symbol symbol, bool expandIncludes, ref string lazyXmlText) { // NOTE: For xml doc comments from source, the culture is ignored (we just return the // doc comment as it appears in source), so it won't affect what we cache. if (lazyXmlText == null) { string xmlText = DocumentationCommentCompiler.GetDocumentationCommentXml(symbol, expandIncludes, default(CancellationToken)); Interlocked.CompareExchange(ref lazyXmlText, xmlText, null); } return lazyXmlText; } internal static ImmutableArray<DocumentationCommentTriviaSyntax> GetDocumentationCommentTriviaFromSyntaxNode(CSharpSyntaxNode syntaxNode, DiagnosticBag diagnostics) { if (syntaxNode.SyntaxTree.Options.DocumentationMode < DocumentationMode.Parse) { return ImmutableArray<DocumentationCommentTriviaSyntax>.Empty; } // All declarators in a declaration get the same doc comment. // (As a consequence, the same duplicate diagnostics are produced for each declarator.) if (syntaxNode.Kind() == SyntaxKind.VariableDeclarator) { CSharpSyntaxNode curr = syntaxNode; while ((object)curr != null) { SyntaxKind kind = curr.Kind(); if (kind == SyntaxKind.FieldDeclaration || kind == SyntaxKind.EventFieldDeclaration) { break; } curr = curr.Parent; } if ((object)curr != null) { syntaxNode = curr; } } ArrayBuilder<DocumentationCommentTriviaSyntax> builder = null; bool seenOtherTrivia = false; foreach (var trivia in syntaxNode.GetLeadingTrivia().Reverse()) { switch (trivia.Kind()) { case SyntaxKind.SingleLineDocumentationCommentTrivia: case SyntaxKind.MultiLineDocumentationCommentTrivia: { if (seenOtherTrivia) { // In most cases, unprocessed doc comments are reported by UnprocessedDocumentationCommentFinder. // However, in places where doc comments *are* allowed, it's easier to determine which will // be unprocessed here. var tree = trivia.SyntaxTree; if (tree.ReportDocumentationCommentDiagnostics()) { int start = trivia.Position; // FullSpan start to include /** or /// const int length = 1; //Match dev11: span is just one character diagnostics.Add(ErrorCode.WRN_UnprocessedXMLComment, new SourceLocation(tree, new TextSpan(start, length))); } } else { if (builder == null) { builder = ArrayBuilder<DocumentationCommentTriviaSyntax>.GetInstance(); } builder.Add((DocumentationCommentTriviaSyntax)trivia.GetStructure()); } break; } case SyntaxKind.WhitespaceTrivia: case SyntaxKind.EndOfLineTrivia: // These can legally appear between doc comments. break; default: // For some reason, dev11 ignores trivia between the last doc comment and the // symbol declaration. (e.g. can have regular comment between doc comment and decl). if (builder != null) { seenOtherTrivia = true; } break; } } if (builder == null) { return ImmutableArray<DocumentationCommentTriviaSyntax>.Empty; } builder.ReverseContents(); return builder.ToImmutableAndFree(); } } }