public void HandleMethodDeclaration(SyntaxNodeAnalysisContext context) { if (context.GetDocumentationMode() != DocumentationMode.Diagnose) { return; } MethodDeclarationSyntax declaration = (MethodDeclarationSyntax)context.Node; if (!declaration.Modifiers.Any(SyntaxKind.PartialKeyword)) { return; } Accessibility declaredAccessibility = declaration.GetDeclaredAccessibility(context.SemanticModel, context.CancellationToken); Accessibility effectiveAccessibility = declaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); if (this.NeedsComment(declaration.Kind(), declaration.Parent.Kind(), declaredAccessibility, effectiveAccessibility)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
public static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings) { if (context.GetDocumentationMode() == DocumentationMode.None) { return; } MethodDeclarationSyntax declaration = (MethodDeclarationSyntax)context.Node; if (!declaration.Modifiers.Any(SyntaxKind.PartialKeyword)) { return; } Accessibility declaredAccessibility = declaration.GetDeclaredAccessibility(context.SemanticModel, context.CancellationToken); Accessibility effectiveAccessibility = declaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken); if (SA1600ElementsMustBeDocumented.NeedsComment(settings.DocumentationRules, declaration.Kind(), declaration.Parent.Kind(), declaredAccessibility, effectiveAccessibility)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
private static void HandleIncludedDocumentation(SyntaxNodeAnalysisContext context, XmlEmptyElementSyntax elementSyntax, Location elementLocation) { var memberDeclaration = elementSyntax.FirstAncestorOrSelf <MemberDeclarationSyntax>(); if (memberDeclaration == null) { return; } var declaration = context.SemanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken); if (declaration == null) { return; } var 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 emptyElements = completeDocumentation.Nodes() .OfType <XElement>() .Where(element => ElementsToCheck.Contains(element.Name.ToString())) .Where(x => XmlCommentHelper.IsConsideredEmpty(x)); foreach (var emptyElement in emptyElements) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, elementLocation, emptyElement.Name.ToString())); } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations) { if (syntax == null) { return; } if (completeDocumentation != null) { XElement summaryNode = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.SummaryXmlTag); if (summaryNode == null) { // Handled by SA1604 return; } if (!XmlCommentHelper.IsConsideredEmpty(summaryNode)) { return; } } else { if (!XmlCommentHelper.IsConsideredEmpty(syntax)) { return; } } foreach (var location in diagnosticLocations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
private static void HandleDocumentation(SyntaxNodeAnalysisContext context) { var documentationTrivia = context.Node as DocumentationCommentTriviaSyntax; if (documentationTrivia != null) { var summaryElement = documentationTrivia.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) as XmlElementSyntax; if (summaryElement != null) { var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax; if (textElement != null) { string text = XmlCommentHelper.GetText(textElement, true); if (!string.IsNullOrEmpty(text)) { if (text.TrimStart().StartsWith(DefaultText, StringComparison.Ordinal)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, summaryElement.GetLocation())); } } } } } }
private static bool IsIgnoredElement(XmlNodeSyntax node) { if (node == null) { return(true); } return(XmlCommentHelper.IsConsideredEmpty(node)); }
private static async Task Exceptions( StreamWriter writer, Assembly assembly, MethodInfo method, XDocument xmlComments) { var exceptions = XmlCommentHelper.MethodElement(xmlComments, method)?.Descendants("exception").ToList(); if (exceptions != null && exceptions.Count > 0) { await writer.WriteLineAsync("### Exceptions"); foreach (var exception in exceptions) { var exceptionTypeName = exception.Attributes("cref").FirstOrDefault()?.Value; if (exceptionTypeName != null) { var exceptionType = TypeHelper.GetType( assembly, exceptionTypeName.Substring(exceptionTypeName.IndexOf(':', StringComparison.Ordinal) + 1)); if (exceptionType != null) { await TypeHelper.FullName(writer, exceptionType, t => t.Name, "<", ">"); } else { var start = exceptionTypeName.LastIndexOf('.'); if (start >= 0) { exceptionTypeName = exceptionTypeName.Substring(start + 1); } var end = exceptionTypeName.IndexOf('`', StringComparison.Ordinal); if (end >= 0) { exceptionTypeName = exceptionTypeName.Substring(0, end); } await writer.WriteAsync(exceptionTypeName); } await writer.WriteLineAsync(" "); await XmlCommentHelper.WriteValue(writer, exception); await writer.WriteLineAsync(); await writer.WriteLineAsync(); } } } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, IEnumerable <XmlNodeSyntax> syntaxList, params Location[] diagnosticLocations) { var node = context.Node; var identifier = GetIdentifier(node); bool supportedIdentifier = identifier != null; if (!supportedIdentifier) { return; } var identifierLocation = identifier.Value.GetLocation(); var parameterList = GetParameters(node)?.ToImmutableArray(); bool hasNoParameters = !parameterList?.Any() ?? false; if (hasNoParameters) { return; } var parentParameters = parameterList.Value; var index = 0; foreach (var syntax in syntaxList) { var nameAttributeSyntax = XmlCommentHelper.GetFirstAttributeOrDefault <XmlNameAttributeSyntax>(syntax); var nameAttributeText = nameAttributeSyntax?.Identifier?.Identifier.ValueText; var location = nameAttributeSyntax?.Identifier?.Identifier.GetLocation(); // Make sure we ignore violations that should be reported by SA1613 instead. if (string.IsNullOrWhiteSpace(nameAttributeText)) { return; } var parentParameter = parentParameters.FirstOrDefault(s => s.Identifier.Text == nameAttributeText); if (parentParameter == null) { context.ReportDiagnostic(Diagnostic.Create(MissingParameterDescriptor, location ?? identifierLocation, nameAttributeText)); } else if (parentParameters.Length <= index || parentParameters[index] != parentParameter) { context.ReportDiagnostic( Diagnostic.Create( OrderDescriptor, location ?? identifierLocation, nameAttributeText, parentParameters.IndexOf(parentParameter) + 1)); } index++; } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, params Location[] diagnosticLocations) { if (syntax != null && XmlCommentHelper.IsConsideredEmpty(syntax)) { foreach (var location in diagnosticLocations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } }
private void HandleXmlElement(SyntaxNodeAnalysisContext context) { XmlElementSyntax emptyElement = context.Node as XmlElementSyntax; var name = emptyElement?.StartTag?.Name; if (string.Equals(name.ToString(), XmlCommentHelper.ReturnsXmlTag) && XmlCommentHelper.IsConsideredEmpty(emptyElement)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, emptyElement.GetLocation())); } }
private static void HandleXmlElement(SyntaxNodeAnalysisContext context) { XmlElementSyntax element = (XmlElementSyntax)context.Node; var name = element.StartTag?.Name; if (string.Equals(name.ToString(), XmlCommentHelper.TypeParamXmlTag) && XmlCommentHelper.IsConsideredEmpty(element)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, element.GetLocation())); } }
/// <summary> /// Analyzes a <see cref="BaseMethodDeclarationSyntax"/> node. If it has a summary it is checked if the text starts with "[firstTextPart]<see cref="[className]"/>[secondTextPart]". /// </summary> /// <param name="context">The <see cref="SyntaxNodeAnalysisContext"/> of this analysis.</param> /// <param name="firstTextPart">The first part of the standard text.</param> /// <param name="secondTextPart">The second part of the standard text.</param> /// <param name="reportDiagnostic">Whether or not a diagnostic should be reported.</param> /// <returns>A <see cref="MatchResult"/> describing the result of the analysis.</returns> protected MatchResult HandleDeclaration(SyntaxNodeAnalysisContext context, string firstTextPart, string secondTextPart, bool reportDiagnostic) { var declarationSyntax = context.Node as BaseMethodDeclarationSyntax; if (declarationSyntax == null) { return(MatchResult.Unknown); } var documentationStructure = declarationSyntax.GetDocumentationCommentTriviaSyntax(); if (documentationStructure == null) { return(MatchResult.Unknown); } var summaryElement = documentationStructure.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) as XmlElementSyntax; if (summaryElement == null) { return(MatchResult.Unknown); } // Check if the summary content could be a correct standard text if (summaryElement.Content.Count >= 3) { // Standard text has the form <part1><see><part2> var firstTextPartSyntax = summaryElement.Content[0] as XmlTextSyntax; var classReferencePart = summaryElement.Content[1] as XmlEmptyElementSyntax; var secondTextParSyntaxt = summaryElement.Content[2] as XmlTextSyntax; if (firstTextPartSyntax != null && classReferencePart != null && secondTextParSyntaxt != null) { // Check text parts var firstText = XmlCommentHelper.GetText(firstTextPartSyntax); var secondText = XmlCommentHelper.GetText(secondTextParSyntaxt); if (TextPartsMatch(firstTextPart, secondTextPart, firstTextPartSyntax, secondTextParSyntaxt) && this.SeeTagIsCorrect(context, classReferencePart, declarationSyntax)) { // We found a correct standard text return(MatchResult.FoundMatch); } } } if (reportDiagnostic) { context.ReportDiagnostic(Diagnostic.Create(this.DiagnosticDescriptor, summaryElement.GetLocation())); } // TODO: be more specific about the type of error when possible return(MatchResult.None); }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, IEnumerable <XmlNodeSyntax> syntaxList, params Location[] diagnosticLocations) { foreach (var syntax in syntaxList) { bool isEmpty = syntax is XmlEmptyElementSyntax || XmlCommentHelper.IsConsideredEmpty(syntax); if (isEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, syntax.GetLocation())); } } }
private static void HandleXmlElement(SyntaxNodeAnalysisContext context) { var element = (XmlElementSyntax)context.Node; var name = element.StartTag?.Name; if (ElementsToCheck.Contains(name.ToString()) && XmlCommentHelper.IsConsideredEmpty(element)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, element.GetLocation(), name.ToString())); } }
private static void HandleElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax element, XmlNameSyntax name, Location alternativeDiagnosticLocation) { if (string.Equals(name.ToString(), XmlCommentHelper.TypeParamXmlTag)) { var nameAttribute = XmlCommentHelper.GetFirstAttributeOrDefault <XmlNameAttributeSyntax>(element); if (string.IsNullOrWhiteSpace(nameAttribute?.Identifier?.Identifier.ValueText)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, nameAttribute?.GetLocation() ?? alternativeDiagnosticLocation)); } } }
private void HandleDestructorDeclaration(SyntaxNodeAnalysisContext context) { DestructorDeclarationSyntax declaration = context.Node as DestructorDeclarationSyntax; if (declaration != null) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
private void HandleConstructorDeclaration(SyntaxNodeAnalysisContext context) { ConstructorDeclarationSyntax declaration = context.Node as ConstructorDeclarationSyntax; if (declaration != null && this.NeedsComment(declaration.Modifiers, SyntaxKind.PrivateKeyword)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
private void HandleEnumMember(SyntaxNodeAnalysisContext context) { EnumMemberDeclarationSyntax enumMemberDeclaration = context.Node as EnumMemberDeclarationSyntax; if (enumMemberDeclaration != null) { if (!XmlCommentHelper.HasDocumentation(enumMemberDeclaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, enumMemberDeclaration.Identifier.GetLocation())); } } }
private static bool TextPartsMatch(string firstText, string secondText, XmlTextSyntax firstTextPart, XmlTextSyntax secondTextPart) { string firstTextPartText = XmlCommentHelper.GetText(firstTextPart, normalizeWhitespace: true); if (firstText != firstTextPartText.TrimStart()) { return(false); } string secondTextPartText = XmlCommentHelper.GetText(secondTextPart, normalizeWhitespace: true); return(secondTextPartText.StartsWith(secondText, StringComparison.Ordinal)); }
private static void HandleElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax element, XmlNameSyntax name, Location alternativeDiagnosticLocation) { if (string.Equals(name.ToString(), XmlCommentHelper.ParamTag)) { var nameAttribute = XmlCommentHelper.GetFirstAttributeOrDefault <XmlNameAttributeSyntax>(element); // Make sure we ignore violations that should be reported by SA1613 instead. if (!string.IsNullOrWhiteSpace(nameAttribute?.Identifier?.Identifier.ValueText) && ParentElementHasParameter(element, nameAttribute.Identifier.Identifier.ValueText) == false) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, nameAttribute?.Identifier?.GetLocation() ?? alternativeDiagnosticLocation)); } } }
private static async Task Returns(StreamWriter writer, MethodInfo method, XDocument xmlComments) { if (method.ReturnType.FullName != "System.Void") { await writer.WriteLineAsync("### Returns"); await TypeHelper.FullName(writer, method.ReturnType, t => t.Name, "<", ">"); await writer.WriteLineAsync(" "); await writer.WriteLineAsync(XmlCommentHelper.Returns(xmlComments, method)); } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, IEnumerable <XmlNodeSyntax> syntaxList, params Location[] diagnosticLocations) { foreach (var syntax in syntaxList) { var nameParameter = XmlCommentHelper.GetFirstAttributeOrDefault <XmlNameAttributeSyntax>(syntax); var parameterValue = nameParameter?.Identifier?.Identifier.ValueText; if (string.IsNullOrWhiteSpace(parameterValue)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, nameParameter?.GetLocation() ?? syntax.GetLocation())); } } }
private void HandleTypeDeclaration(SyntaxNodeAnalysisContext context) { BaseTypeDeclarationSyntax declaration = context.Node as BaseTypeDeclarationSyntax; bool isNestedInClassOrStruct = this.IsNestedType(declaration); if (declaration != null && this.NeedsComment(declaration.Modifiers, isNestedInClassOrStruct ? SyntaxKind.PrivateKeyword : SyntaxKind.InternalKeyword)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, declaration.Identifier.GetLocation())); } } }
/// <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())); } } }
private void HandleMethodDeclaration(SyntaxNodeAnalysisContext context) { MethodDeclarationSyntax methodDeclaration = context.Node as MethodDeclarationSyntax; if (methodDeclaration != null) { if (methodDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) { if (!XmlCommentHelper.HasDocumentation(methodDeclaration)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, methodDeclaration.Identifier.GetLocation())); } } } }
private static async Task Properties(StreamWriter writer, Type type, XDocument xmlComments) { var properties = type.GetProperties().Where(TypeHelper.IgnoreDeclaringType).ToList(); if (properties.Count > 0) { await writer.WriteLineAsync("### Properties"); await writer.WriteLineAsync("| | |"); await writer.WriteLineAsync("|_|_|"); foreach (var property in properties.OrderBy(o => o.Name)) { await writer.WriteAsync("["); await writer.WriteAsync(property.Name); await writer.WriteAsync("]("); await writer.WriteAsync(FileNameHelper.PropertyFileName(string.Empty, property)); await writer.WriteAsync(")|"); var summary = XmlCommentHelper.Property(xmlComments, property); await writer.WriteAsync(summary); if (property.DeclaringType != type) { if (summary.Length > 0) { await writer.WriteAsync("<br/>"); } await writer.WriteAsync("(Inherited from "); await writer.WriteAsync(property.DeclaringType?.Name); await writer.WriteAsync(")"); } await writer.WriteLineAsync(); } } await writer.WriteLineAsync(); }
/// <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())); } } }
/// <inheritdoc/> protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, StyleCopSettings settings, bool needsComment, IEnumerable <XmlNodeSyntax> syntaxList, params Location[] diagnosticLocations) { foreach (var syntax in syntaxList) { var summaryElement = syntax as XmlElementSyntax; if (summaryElement?.Content.FirstOrDefault() is XmlTextSyntax textElement) { string text = XmlCommentHelper.GetText(textElement, true); if (IsDefaultText(text)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, summaryElement.GetLocation())); return; } } } }
private void HandleFieldDeclaration(SyntaxNodeAnalysisContext context) { FieldDeclarationSyntax declaration = context.Node as FieldDeclarationSyntax; var variableDeclaration = declaration?.Declaration; if (variableDeclaration != null && this.NeedsComment(declaration.Modifiers, SyntaxKind.PrivateKeyword)) { if (!XmlCommentHelper.HasDocumentation(declaration)) { var locations = variableDeclaration.Variables.Select(v => v.Identifier.GetLocation()).ToArray(); foreach (var location in locations) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } } }
private static async Task Constructors(StreamWriter writer, Type type, XDocument xmlComments) { var constructors = type.GetConstructors(); if (constructors.Length > 0) { await writer.WriteLineAsync("### Constructors"); await writer.WriteLineAsync("| | |"); await writer.WriteLineAsync("|_|_|"); foreach (var constructor in constructors) { await writer.WriteAsync("["); await TypeHelper.FullName(writer, type, t => t.Name, "<", ">"); await writer.WriteAsync("("); var parameters = constructor.GetParameters(); if (parameters.Length > 0) { await TypeHelper.FullName(writer, parameters[0].ParameterType, t => t.Name, "<", ">"); for (var i = 1; i < parameters.Length; i++) { await writer.WriteAsync(", "); await TypeHelper.FullName(writer, parameters[i].ParameterType, t => t.Name, "<", ">"); } } await writer.WriteAsync(")]("); await writer.WriteAsync(FileNameHelper.ConstructorFileName(string.Empty, type)); await writer.WriteAsync(")|"); await writer.WriteLineAsync( XmlCommentHelper.Summary(XmlCommentHelper.MethodElement(xmlComments, constructor))); } } }