/// <inheritdoc/>
 protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location[] diagnosticLocations)
 {
     if (syntax != null)
     {
         if (XmlCommentHelper.IsConsideredEmpty(syntax))
         {
             foreach (var location in diagnosticLocations)
             {
                 context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
             }
         }
     }
 }
        /// <inheritdoc/>
        protected override void HandleCompleteDocumentation(SyntaxNodeAnalysisContext context, XElement completeDocumentation, params Location[] diagnosticLocations)
        {
            var returnsNodes = completeDocumentation.Nodes()
                               .OfType <XElement>()
                               .Where(n => n.Name == XmlCommentHelper.ReturnsXmlTag);

            foreach (var node in returnsNodes)
            {
                if (XmlCommentHelper.IsConsideredEmpty(node))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocations.First()));
                }
            }
        }
Example #3
0
        /// <inheritdoc/>
        protected override void HandleCompleteDocumentation(SyntaxNodeAnalysisContext context, bool needsComment, XElement completeDocumentation, params Location[] diagnosticLocations)
        {
            var xmlParamTags = completeDocumentation.Nodes()
                               .OfType <XElement>()
                               .Where(e => e.Name == XmlCommentHelper.ParamXmlTag);

            foreach (var paramTag in xmlParamTags)
            {
                bool isEmpty = XmlCommentHelper.IsConsideredEmpty(paramTag);

                if (isEmpty)
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocations.First()));
                }
            }
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var node          = root.FindNode(diagnostic.Location.SourceSpan);
            var documentation = node.GetDocumentationCommentTriviaSyntax();

            // Check if the return value is documented
            var returnsElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.ReturnsXmlTag);

            if (returnsElement == null)
            {
                return(document);
            }

            // Find the node previous to the <returns> node to determine if it is an XML comment indicator. If so, we
            // will remove that node from the syntax tree as well.
            SyntaxNode previous = null;

            foreach (var item in documentation.ChildNodes())
            {
                if (item.Equals(returnsElement))
                {
                    break;
                }

                previous = item;
            }

            List <SyntaxNode> nodesToFix = new List <SyntaxNode>();

            nodesToFix.Add(returnsElement);

            var previousAsTextSyntax = previous as XmlTextSyntax;

            if (previousAsTextSyntax != null && XmlCommentHelper.IsConsideredEmpty(previousAsTextSyntax))
            {
                nodesToFix.Add(previous);
            }

            var newSyntaxRoot = root.RemoveNodes(nodesToFix, SyntaxRemoveOptions.KeepLeadingTrivia);

            return(document.WithSyntaxRoot(newSyntaxRoot));
        }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations)
        {
            if (completeDocumentation != null)
            {
                var summaryTag = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.SummaryXmlTag);
                var contentTag = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.ContentXmlTag);

                if ((summaryTag == null) && (contentTag == null))
                {
                    // handled by SA1605
                    return;
                }

                if (!XmlCommentHelper.IsConsideredEmpty(summaryTag) || !XmlCommentHelper.IsConsideredEmpty(contentTag))
                {
                    return;
                }
            }
            else
            {
                if (syntax == null)
                {
                    // handled by SA1605
                    return;
                }

                if (!XmlCommentHelper.IsConsideredEmpty(syntax))
                {
                    return;
                }
            }

            foreach (var location in diagnosticLocations)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
        {
            var properties = ImmutableDictionary.Create <string, string>();

            if (completeDocumentation != null)
            {
                var valueTag = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.ValueXmlTag);
                if (valueTag == null)
                {
                    // handled by SA1609
                    return;
                }

                if (!XmlCommentHelper.IsConsideredEmpty(valueTag))
                {
                    return;
                }

                properties = properties.Add(NoCodeFixKey, string.Empty);
            }
            else
            {
                if (syntax == null)
                {
                    // Handled by SA1609
                    return;
                }

                if (!XmlCommentHelper.IsConsideredEmpty(syntax))
                {
                    return;
                }
            }

            context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation, properties));
        }
        private static XmlElementSyntax TrimWhitespaceContent(XmlElementSyntax paragraph, out SyntaxList <XmlNodeSyntax> leadingWhitespaceContent, out SyntaxList <XmlNodeSyntax> trailingWhitespaceContent)
        {
            SyntaxList <XmlNodeSyntax> completeContent = XmlSyntaxFactory.List(paragraph.Content.SelectMany(ExpandTextNodes).ToArray());

            leadingWhitespaceContent  = XmlSyntaxFactory.List(completeContent.TakeWhile(x => XmlCommentHelper.IsConsideredEmpty(x)).ToArray());
            trailingWhitespaceContent = XmlSyntaxFactory.List(completeContent.Skip(leadingWhitespaceContent.Count).Reverse().TakeWhile(x => XmlCommentHelper.IsConsideredEmpty(x)).Reverse().ToArray());

            SyntaxList <XmlNodeSyntax> trimmedContent = XmlSyntaxFactory.List(completeContent.Skip(leadingWhitespaceContent.Count).Take(completeContent.Count - leadingWhitespaceContent.Count - trailingWhitespaceContent.Count).ToArray());
            SyntaxTriviaList           leadingTrivia  = SyntaxFactory.TriviaList();
            SyntaxTriviaList           trailingTrivia = SyntaxFactory.TriviaList();

            if (trimmedContent.Any())
            {
                leadingTrivia  = trimmedContent[0].GetLeadingTrivia();
                trailingTrivia = trimmedContent.Last().GetTrailingTrivia();
                trimmedContent = trimmedContent.Replace(trimmedContent[0], trimmedContent[0].WithoutLeadingTrivia());
                trimmedContent = trimmedContent.Replace(trimmedContent.Last(), trimmedContent.Last().WithoutTrailingTrivia());
            }
            else
            {
                leadingTrivia  = SyntaxFactory.TriviaList();
                trailingTrivia = SyntaxFactory.TriviaList();
            }

            XmlElementSyntax result = paragraph;

            if (leadingWhitespaceContent.Any())
            {
                var first    = leadingWhitespaceContent[0];
                var newFirst = first.WithLeadingTrivia(first.GetLeadingTrivia().InsertRange(0, paragraph.GetLeadingTrivia()));
                leadingWhitespaceContent = leadingWhitespaceContent.Replace(first, newFirst);
            }
            else
            {
                leadingTrivia = leadingTrivia.InsertRange(0, result.GetLeadingTrivia());
            }

            if (trailingWhitespaceContent.Any())
            {
                var last    = trailingWhitespaceContent.Last();
                var newLast = last.WithLeadingTrivia(last.GetLeadingTrivia().AddRange(paragraph.GetTrailingTrivia()));
                trailingWhitespaceContent = trailingWhitespaceContent.Replace(last, newLast);
            }
            else
            {
                trailingTrivia = trailingTrivia.AddRange(result.GetTrailingTrivia());
            }

            if (trimmedContent.FirstOrDefault() is XmlTextSyntax firstTextNode &&
                firstTextNode.TextTokens.Any())
            {
                SyntaxToken firstTextToken    = firstTextNode.TextTokens[0];
                string      leadingWhitespace = new(firstTextToken.Text.Cast <char>().TakeWhile(char.IsWhiteSpace).ToArray());
                if (leadingWhitespace.Length > 0)
                {
                    SyntaxToken   newFirstTextToken = XmlSyntaxFactory.TextLiteral(firstTextToken.Text.Substring(leadingWhitespace.Length)).WithTriviaFrom(firstTextToken);
                    XmlTextSyntax newFirstTextNode  = firstTextNode.WithTextTokens(firstTextNode.TextTokens.Replace(firstTextToken, newFirstTextToken));
                    trimmedContent = trimmedContent.Replace(firstTextNode, newFirstTextNode);
                    leadingTrivia  = leadingTrivia.Add(SyntaxFactory.Whitespace(leadingWhitespace));
                }
            }

            if (trimmedContent.LastOrDefault() is XmlTextSyntax lastTextNode &&
                lastTextNode.TextTokens.Any())
            {
                SyntaxToken lastTextToken      = lastTextNode.TextTokens.Last();
                string      trailingWhitespace = new(lastTextToken.Text.Cast <char>().Reverse().TakeWhile(char.IsWhiteSpace).Reverse().ToArray());
                if (trailingWhitespace.Length > 0)
                {
                    SyntaxToken   newLastTextToken = XmlSyntaxFactory.TextLiteral(lastTextToken.Text.Substring(0, lastTextToken.Text.Length - trailingWhitespace.Length)).WithTriviaFrom(lastTextToken);
                    XmlTextSyntax newLastTextNode  = lastTextNode.WithTextTokens(lastTextNode.TextTokens.Replace(lastTextToken, newLastTextToken));
                    trimmedContent = trimmedContent.Replace(lastTextNode, newLastTextNode);
                    trailingTrivia = trailingTrivia.Insert(0, SyntaxFactory.Whitespace(trailingWhitespace));
                }
            }

            return(result.WithContent(trimmedContent)
                   .WithLeadingTrivia(leadingTrivia)
                   .WithTrailingTrivia(trailingTrivia));
        }
        private static void HandleDeclaration(SyntaxNodeAnalysisContext context, SyntaxNode node, TypeParameterListSyntax typeParameterList)
        {
            if (typeParameterList == null)
            {
                // The node does not have a type parameter list
                return;
            }

            var documentation = node.GetDocumentationCommentTriviaSyntax();

            if (documentation == null)
            {
                // Don't report if the documentation is missing
                return;
            }

            if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag) != null)
            {
                // Ignore nodes with an <inheritdoc/> tag.
                return;
            }

            var includeElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag);

            if (includeElement != null)
            {
                string rawDocumentation;
                var    declaration = context.SemanticModel.GetDeclaredSymbol(context.Node, context.CancellationToken);
                if (declaration == null)
                {
                    return;
                }

                rawDocumentation = declaration.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
                var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);
                if (completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
                {
                    // Ignore nodes with an <inheritdoc/> tag in the included XML.
                    return;
                }

                var typeParameterAttributes = completeDocumentation.Nodes()
                                              .OfType <XElement>()
                                              .Where(element => element.Name == XmlCommentHelper.TypeParamXmlTag)
                                              .ToImmutableArray();

                // Check based on the documented type parameters, as we must detect scenarios where there are too many type parameters documented.
                // It is not necessary to detect missing type parameter documentation, this belongs to SA1618.
                for (var i = 0; i < typeParameterAttributes.Length; i++)
                {
                    var documentedParameterName = typeParameterAttributes[i].Attribute(XmlCommentHelper.NameArgumentName)?.Value;
                    HandleTypeParamElement(context, documentedParameterName, i, typeParameterList, includeElement.GetLocation());

                    if (XmlCommentHelper.IsConsideredEmpty(typeParameterAttributes[i]))
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                SA1622Descriptor,
                                includeElement.GetLocation()));
                    }
                }
            }
            else
            {
                var typeParameterTags = documentation.Content.GetXmlElements(XmlCommentHelper.TypeParamXmlTag)
                                        .ToImmutableArray();

                // Check based on the documented type parameters, as we must detect scenarios where there are too many type parameters documented.
                // It is not necessary to detect missing type parameter documentation, this belongs to SA1618.
                for (var i = 0; i < typeParameterTags.Length; i++)
                {
                    var nameAttribute           = XmlCommentHelper.GetFirstAttributeOrDefault <XmlNameAttributeSyntax>(typeParameterTags[i]);
                    var documentedParameterName = nameAttribute?.Identifier?.Identifier.ValueText;

                    var location = nameAttribute?.Identifier?.GetLocation() ?? typeParameterTags[i].GetLocation();
                    HandleTypeParamElement(context, documentedParameterName, i, typeParameterList, location);

                    if (XmlCommentHelper.IsConsideredEmpty(typeParameterTags[i], true))
                    {
                        context.ReportDiagnostic(
                            Diagnostic.Create(
                                SA1622Descriptor,
                                typeParameterTags[i].GetLocation()));
                    }
                }
            }
        }