public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out XmlNodeSyntax xmlNode, findInsideTrivia: true)) { return; } Document document = context.Document; foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.UnusedElementInDocumentationComment: { XmlElementInfo elementInfo = SyntaxInfo.XmlElementInfo(xmlNode); string name = elementInfo.LocalName; CodeAction codeAction = CodeAction.Create( $"Remove element '{name}'", ct => RemoveUnusedElementInDocumentationCommentAsync(document, elementInfo, ct), GetEquivalenceKey(diagnostic, name)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.FixDocumentationCommentTag: { XmlElementInfo elementInfo = SyntaxInfo.XmlElementInfo(xmlNode); CodeAction codeAction = CodeAction.Create( (elementInfo.GetTag() == XmlTag.C) ? "Rename tag to 'code'" : "Rename tag to 'c'", ct => FixDocumentationCommentTagAsync(document, elementInfo, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
public static void Analyze(SyntaxNodeAnalysisContext context, XmlElementInfo elementInfo) { if (elementInfo.IsEmptyElement) { return; } var element = (XmlElementSyntax)elementInfo.Element; foreach (XmlNodeSyntax node in element.Content) { XmlElementInfo elementInfo2 = SyntaxInfo.XmlElementInfo(node); if (elementInfo2.Success) { switch (elementInfo2.GetTag()) { case XmlTag.C: { AnalyzeCElement(context, elementInfo2); break; } case XmlTag.Code: { AnalyzeCodeElement(context, elementInfo2); break; } case XmlTag.List: { AnalyzeList(context, elementInfo2); break; } case XmlTag.Para: case XmlTag.ParamRef: case XmlTag.See: case XmlTag.TypeParamRef: { Analyze(context, elementInfo2); break; } } } } }
private static bool CanAddExceptionToComment( DocumentationCommentTriviaSyntax comment, INamedTypeSymbol exceptionSymbol, SemanticModel semanticModel, CancellationToken cancellationToken) { var containsException = false; var containsIncludeOrExclude = false; var isFirst = true; foreach (XmlNodeSyntax node in comment.Content) { XmlElementInfo info = SyntaxInfo.XmlElementInfo(node); if (info.Success) { switch (info.GetTag()) { case XmlTag.Include: case XmlTag.Exclude: { if (isFirst) { containsIncludeOrExclude = true; } break; } case XmlTag.InheritDoc: { return(false); } case XmlTag.Exception: { if (!containsException) { if (info.IsEmptyElement) { containsException = ContainsException((XmlEmptyElementSyntax)info.Element, exceptionSymbol, semanticModel, cancellationToken); } else { containsException = ContainsException((XmlElementSyntax)info.Element, exceptionSymbol, semanticModel, cancellationToken); } } break; } } if (isFirst) { isFirst = false; } else { containsIncludeOrExclude = false; } } } return(!containsIncludeOrExclude && !containsException); }
private static void AnalyzeSingleLineDocumentationCommentTrivia(SyntaxNodeAnalysisContext context) { var documentationComment = (DocumentationCommentTriviaSyntax)context.Node; if (!documentationComment.IsPartOfMemberDeclaration()) { return; } bool?useCorrectDocumentationTagEnabled = null; var containsInheritDoc = false; var containsIncludeOrExclude = false; var containsSummaryElement = false; var containsContentElement = false; var isFirst = true; CancellationToken cancellationToken = context.CancellationToken; SyntaxList <XmlNodeSyntax> content = documentationComment.Content; for (int i = 0; i < content.Count; i++) { cancellationToken.ThrowIfCancellationRequested(); XmlElementInfo info = SyntaxInfo.XmlElementInfo(content[i]); if (info.Success) { switch (info.GetTag()) { case XmlTag.Include: case XmlTag.Exclude: { if (isFirst) { containsIncludeOrExclude = true; } break; } case XmlTag.InheritDoc: { containsInheritDoc = true; break; } case XmlTag.Content: { containsContentElement = true; break; } case XmlTag.Summary: { if (info.IsContentEmptyOrWhitespace) { ReportDiagnosticIfNotSuppressed(context, DiagnosticRules.AddSummaryToDocumentationComment, info.Element); } containsSummaryElement = true; if (useCorrectDocumentationTagEnabled ??= DiagnosticRules.FixDocumentationCommentTag.IsEffective(context)) { FixDocumentationCommentTagAnalysis.Analyze(context, info); } break; } case XmlTag.Example: case XmlTag.Remarks: case XmlTag.Returns: case XmlTag.Value: { if (info.IsContentEmptyOrWhitespace) { ReportUnusedElement(context, info.Element, i, content); } if (useCorrectDocumentationTagEnabled ??= DiagnosticRules.FixDocumentationCommentTag.IsEffective(context)) { FixDocumentationCommentTagAnalysis.Analyze(context, info); } break; } case XmlTag.Exception: case XmlTag.List: case XmlTag.Param: case XmlTag.Permission: case XmlTag.TypeParam: { if (useCorrectDocumentationTagEnabled ??= DiagnosticRules.FixDocumentationCommentTag.IsEffective(context)) { FixDocumentationCommentTagAnalysis.Analyze(context, info); } break; } case XmlTag.C: case XmlTag.Code: case XmlTag.Para: case XmlTag.ParamRef: case XmlTag.See: case XmlTag.SeeAlso: case XmlTag.TypeParamRef: { break; } default: { Debug.Fail(info.GetTag().ToString()); break; } } if (isFirst) { isFirst = false; } else { containsIncludeOrExclude = false; } } } if (containsInheritDoc || containsIncludeOrExclude) { return; } if (!containsSummaryElement && !containsContentElement) { ReportDiagnosticIfNotSuppressed(context, DiagnosticRules.AddSummaryElementToDocumentationComment, documentationComment); } SyntaxNode parent = documentationComment.ParentTrivia.Token.Parent; bool unusedElement = DiagnosticRules.UnusedElementInDocumentationComment.IsEffective(context); bool orderParams = DiagnosticRules.OrderElementsInDocumentationComment.IsEffective(context); bool addParam = DiagnosticRules.AddParamElementToDocumentationComment.IsEffective(context); bool addTypeParam = DiagnosticRules.AddTypeParamElementToDocumentationComment.IsEffective(context); if (addParam || orderParams || unusedElement) { SeparatedSyntaxList <ParameterSyntax> parameters = CSharpUtility.GetParameters( (parent is MemberDeclarationSyntax) ? parent : parent.Parent); if (addParam && parameters.Any()) { foreach (ParameterSyntax parameter in parameters) { if (IsMissing(documentationComment, parameter)) { ReportDiagnostic(context, DiagnosticRules.AddParamElementToDocumentationComment, documentationComment); break; } } } if (orderParams || unusedElement) { Analyze(context, documentationComment.Content, parameters, XmlTag.Param, (nodes, name) => nodes.IndexOf(name)); } } if (addTypeParam || orderParams || unusedElement) { SeparatedSyntaxList <TypeParameterSyntax> typeParameters = CSharpUtility.GetTypeParameters( (parent is MemberDeclarationSyntax) ? parent : parent.Parent); if (addTypeParam && typeParameters.Any()) { foreach (TypeParameterSyntax typeParameter in typeParameters) { if (IsMissing(documentationComment, typeParameter)) { ReportDiagnostic(context, DiagnosticRules.AddTypeParamElementToDocumentationComment, documentationComment); break; } } } if (orderParams || unusedElement) { Analyze(context, documentationComment.Content, typeParameters, XmlTag.TypeParam, (nodes, name) => nodes.IndexOf(name)); } } }
private static void AnalyzeSingleLineDocumentationCommentTrivia(SyntaxNodeAnalysisContext context) { var documentationComment = (DocumentationCommentTriviaSyntax)context.Node; if (!documentationComment.IsPartOfMemberDeclaration()) { return; } bool containsInheritDoc = false; bool containsIncludeOrExclude = false; bool containsSummaryElement = false; bool containsContentElement = false; bool isFirst = true; CancellationToken cancellationToken = context.CancellationToken; SyntaxList <XmlNodeSyntax> content = documentationComment.Content; for (int i = 0; i < content.Count; i++) { cancellationToken.ThrowIfCancellationRequested(); XmlElementInfo info = SyntaxInfo.XmlElementInfo(content[i]); if (info.Success) { switch (info.GetTag()) { case XmlTag.Include: case XmlTag.Exclude: { if (isFirst) { containsIncludeOrExclude = true; } break; } case XmlTag.InheritDoc: { containsInheritDoc = true; break; } case XmlTag.Content: { containsContentElement = true; break; } case XmlTag.Summary: { if (info.IsContentEmptyOrWhitespace) { ReportDiagnosticIfNotSuppressed(context, DiagnosticDescriptors.AddSummaryToDocumentationComment, info.Element); } containsSummaryElement = true; break; } case XmlTag.Code: case XmlTag.Example: case XmlTag.Remarks: case XmlTag.Returns: case XmlTag.Value: { if (info.IsContentEmptyOrWhitespace) { ReportUnusedElement(context, info.Element, i, content); } break; } } if (isFirst) { isFirst = false; } else { containsIncludeOrExclude = false; } } } if (containsInheritDoc || containsIncludeOrExclude) { return; } if (!containsSummaryElement && !containsContentElement) { ReportDiagnosticIfNotSuppressed(context, DiagnosticDescriptors.AddSummaryElementToDocumentationComment, documentationComment); } SyntaxNode parent = documentationComment.ParentTrivia.Token.Parent; bool unusedElement = !context.IsAnalyzerSuppressed(DiagnosticDescriptors.UnusedElementInDocumentationComment); bool orderParams = !context.IsAnalyzerSuppressed(DiagnosticDescriptors.OrderElementsInDocumentationComment); bool addParam = !context.IsAnalyzerSuppressed(DiagnosticDescriptors.AddParamElementToDocumentationComment); bool addTypeParam = !context.IsAnalyzerSuppressed(DiagnosticDescriptors.AddTypeParamElementToDocumentationComment); if (addParam || orderParams || unusedElement) { SeparatedSyntaxList <ParameterSyntax> parameters = CSharpUtility.GetParameters((CSharpFacts.HasParameterList(parent.Kind())) ? parent : parent.Parent); if (addParam && parameters.Any()) { foreach (ParameterSyntax parameter in parameters) { if (IsMissing(documentationComment, parameter)) { ReportDiagnostic(context, DiagnosticDescriptors.AddParamElementToDocumentationComment, documentationComment); break; } } } if (orderParams || unusedElement) { Analyze(context, documentationComment.Content, parameters, XmlTag.Param, (nodes, name) => nodes.IndexOf(name)); } } if (addTypeParam || orderParams || unusedElement) { SeparatedSyntaxList <TypeParameterSyntax> typeParameters = CSharpUtility.GetTypeParameters((CSharpFacts.HasTypeParameterList(parent.Kind())) ? parent : parent.Parent); if (addTypeParam && typeParameters.Any()) { foreach (TypeParameterSyntax typeParameter in typeParameters) { if (IsMissing(documentationComment, typeParameter)) { ReportDiagnostic(context, DiagnosticDescriptors.AddTypeParamElementToDocumentationComment, documentationComment); break; } } } if (orderParams || unusedElement) { Analyze(context, documentationComment.Content, typeParameters, XmlTag.TypeParam, (nodes, name) => nodes.IndexOf(name)); } } }
public static void Analyze(SyntaxNodeAnalysisContext context, XmlElementInfo elementInfo) { if (elementInfo.IsEmptyElement) { return; } var element = (XmlElementSyntax)elementInfo.Element; foreach (XmlNodeSyntax node in element.Content) { XmlElementInfo elementInfo2 = SyntaxInfo.XmlElementInfo(node); if (elementInfo2.Success) { switch (elementInfo2.GetTag()) { case XmlTag.C: { AnalyzeCElement(context, elementInfo2); break; } case XmlTag.Code: { AnalyzeCodeElement(context, elementInfo2); break; } case XmlTag.List: { AnalyzeList(context, elementInfo2); break; } case XmlTag.Para: case XmlTag.ParamRef: case XmlTag.See: case XmlTag.TypeParamRef: { Analyze(context, elementInfo2); break; } case XmlTag.Content: case XmlTag.Example: case XmlTag.Exception: case XmlTag.Exclude: case XmlTag.Include: case XmlTag.InheritDoc: case XmlTag.Param: case XmlTag.Permission: case XmlTag.Remarks: case XmlTag.Returns: case XmlTag.SeeAlso: case XmlTag.Summary: case XmlTag.TypeParam: case XmlTag.Value: { break; } default: { break; } } } } }