/// <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())); } } }
/// <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())); } } } }