/// <inheritdoc/>
 protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
 {
     if (syntax != null && XmlCommentHelper.IsConsideredEmpty(syntax))
     {
         context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation));
     }
 }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, 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));
            }
        }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations)
        {
            if (completeDocumentation != null)
            {
                // We are working with an <include> element
                if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.SummaryXmlTag))
                {
                    return;
                }

                if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
                {
                    // Ignore nodes with an <inheritdoc/> tag in the included XML.
                    return;
                }
            }
            else
            {
                if (syntax != null)
                {
                    return;
                }

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

            foreach (var location in diagnosticLocations)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }
 /// <inheritdoc/>
 protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
 {
     if (syntax == null)
     {
         context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation));
     }
 }
 public XmlAttributeSyntax(XmlNameSyntax name, PunctuationSyntax equals, XmlNodeSyntax value)
     : base(SyntaxKind.XmlAttribute)
 {
     this.NameNode = name;
     this.Equals = equals;
     this.ValueNode = value;
     SlotCount = 3;
 }
 /// <inheritdoc/>
 protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, params Location[] diagnosticLocations)
 {
     if (syntax == null)
     {
         foreach (var location in diagnosticLocations)
         {
             context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
         }
     }
 }
        private static void HandleElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax element, XmlNameSyntax name, Location alternativeDiagnosticLocation)
        {
            if (string.Equals(name.ToString(), XmlCommentHelper.ParamXmlTag))
            {
                var nameAttribute = XmlCommentHelper.GetFirstAttributeOrDefault<XmlNameAttributeSyntax>(element);

                if (string.IsNullOrWhiteSpace(nameAttribute?.Identifier?.Identifier.ValueText))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptor, nameAttribute?.GetLocation() ?? alternativeDiagnosticLocation));
                }
            }
        }
        private static bool IsContentElement(XmlNodeSyntax syntax)
        {
            switch (syntax.Kind())
            {
            case SyntaxKind.XmlCDataSection:
            case SyntaxKind.XmlElement:
            case SyntaxKind.XmlEmptyElement:
            case SyntaxKind.XmlText:
                return true;

            default:
                return false;
            }
        }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
        {
            var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
            var propertyType = context.SemanticModel.GetTypeInfo(propertyDeclaration.Type);

            if (propertyType.Type.SpecialType == SpecialType.System_Boolean)
            {
                AnalyzeSummaryElement(context, syntax, diagnosticLocation, propertyDeclaration, StartingTextGetsWhether, StartingTextSetsWhether, StartingTextGetsOrSetsWhether);
            }
            else
            {
                AnalyzeSummaryElement(context, syntax, diagnosticLocation, propertyDeclaration, StartingTextGets, StartingTextSets, StartingTextGetsOrSets);
            }
        }
 public XmlDocumentSyntax(
     SyntaxKind kind,
     XmlDeclarationSyntax prologue,
     SyntaxNode precedingMisc,
     XmlNodeSyntax body,
     SyntaxNode followingMisc,
     SyntaxToken eof)
     : base(kind)
 {
     this.Prologue = prologue;
     this.PrecedingMisc = precedingMisc;
     this.Body = body;
     this.FollowingMisc = followingMisc;
     this.Eof = eof;
     SlotCount = 5;
 }
 private void ClassifyXmlNode(XmlNodeSyntax node)
 {
     switch (node.Kind())
     {
         case SyntaxKind.XmlElement:
             ClassifyXmlElement((XmlElementSyntax)node);
             break;
         case SyntaxKind.XmlEmptyElement:
             ClassifyXmlEmptyElement((XmlEmptyElementSyntax)node);
             break;
         case SyntaxKind.XmlText:
             ClassifyXmlText((XmlTextSyntax)node);
             break;
         case SyntaxKind.XmlComment:
             ClassifyXmlComment((XmlCommentSyntax)node);
             break;
         case SyntaxKind.XmlCDataSection:
             ClassifyXmlCDataSection((XmlCDataSectionSyntax)node);
             break;
         case SyntaxKind.XmlProcessingInstruction:
             ClassifyXmlProcessingInstruction((XmlProcessingInstructionSyntax)node);
             break;
     }
 }
示例#12
0
 private string Clean(XmlNodeSyntax xmlNodeSyntax) => xmlNodeSyntax.ToString().Replace("\r", "").Replace("\n", "").Replace("///", "").Trim();
 public XmlNodeSyntax Visit(XmlNodeSyntax node)
 {
     return node;
 }
        private static void HandleMemberDeclaration(SyntaxNodeAnalysisContext context)
        {
            MemberDeclarationSyntax memberSyntax = (MemberDeclarationSyntax)context.Node;

            var modifiers = memberSyntax.GetModifiers();

            if (modifiers.Any(SyntaxKind.OverrideKeyword))
            {
                return;
            }

            DocumentationCommentTriviaSyntax documentation = memberSyntax.GetDocumentationCommentTriviaSyntax();

            if (documentation == null)
            {
                return;
            }

            Location location;

            ISymbol declaredSymbol = context.SemanticModel.GetDeclaredSymbol(memberSyntax, context.CancellationToken);

            if (declaredSymbol == null && memberSyntax.IsKind(SyntaxKind.EventFieldDeclaration))
            {
                var eventFieldDeclarationSyntax        = (EventFieldDeclarationSyntax)memberSyntax;
                VariableDeclaratorSyntax firstVariable = eventFieldDeclarationSyntax.Declaration?.Variables.FirstOrDefault();
                if (firstVariable != null)
                {
                    declaredSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable, context.CancellationToken);
                }
            }

            if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag) is XmlEmptyElementSyntax includeElement)
            {
                if (declaredSymbol == null)
                {
                    return;
                }

                var rawDocumentation      = declaredSymbol.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
                var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);

                var inheritDocElement = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.InheritdocXmlTag);
                if (inheritDocElement == null)
                {
                    return;
                }

                if (HasXmlCrefAttribute(inheritDocElement))
                {
                    return;
                }

                location = includeElement.GetLocation();
            }
            else
            {
                XmlNodeSyntax inheritDocElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag);
                if (inheritDocElement == null)
                {
                    return;
                }

                if (HasXmlCrefAttribute(inheritDocElement))
                {
                    return;
                }

                location = inheritDocElement.GetLocation();
            }

            // If we don't have a declared symbol we have some kind of field declaration. A field can not override or
            // implement anything so we want to report a diagnostic.
            if (declaredSymbol == null || !NamedTypeHelpers.IsImplementingAnInterfaceMember(declaredSymbol))
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }
        private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation, PropertyDeclarationSyntax propertyDeclaration, string startingTextGets, string startingTextSets, string startingTextGetsOrSets)
        {
            var diagnosticProperties = ImmutableDictionary.CreateBuilder<string, string>();
            ArrowExpressionClauseSyntax expressionBody = propertyDeclaration.ExpressionBody;
            AccessorDeclarationSyntax getter = null;
            AccessorDeclarationSyntax setter = null;

            if (propertyDeclaration.AccessorList != null)
            {
                foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
                {
                    switch (accessor.Keyword.Kind())
                    {
                    case SyntaxKind.GetKeyword:
                        getter = accessor;
                        break;

                    case SyntaxKind.SetKeyword:
                        setter = accessor;
                        break;
                    }
                }
            }

            XmlElementSyntax summaryElement = (XmlElementSyntax)syntax;
            if (summaryElement == null)
            {
                // This is reported by SA1604.
                return;
            }

            // Add a no code fix tag when the summary element is empty.
            // This will only impact SA1623, because SA1624 cannot trigger with an empty summary.
            if (summaryElement.Content.Count == 0)
            {
                diagnosticProperties.Add(NoCodeFixKey, string.Empty);
            }

            var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax;
            var text = textElement == null ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart();

            if (getter != null || expressionBody != null)
            {
                bool startsWithGetOrSet = text.StartsWith(startingTextGetsOrSets, StringComparison.Ordinal);

                if (setter != null)
                {
                    // There is no way getter is null (can't have expression body and accessor list)
                    bool getterVisible;
                    bool setterVisible;

                    if (!getter.Modifiers.Any() && !setter.Modifiers.Any())
                    {
                        // Case 1: The getter and setter have the same declared accessibility
                        getterVisible = true;
                        setterVisible = true;
                    }
                    else if (getter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                    {
                        // Case 3
                        getterVisible = false;
                        setterVisible = true;
                    }
                    else if (setter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                    {
                        // Case 3
                        getterVisible = true;
                        setterVisible = false;
                    }
                    else
                    {
                        var propertyAccessibility = propertyDeclaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                        bool propertyOnlyInternal = propertyAccessibility == Accessibility.Internal
                            || propertyAccessibility == Accessibility.ProtectedAndInternal
                            || propertyAccessibility == Accessibility.Private;
                        if (propertyOnlyInternal)
                        {
                            // Case 2: Property only internal and no accessor is explicitly private
                            getterVisible = true;
                            setterVisible = true;
                        }
                        else
                        {
                            var getterAccessibility = getter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                            var setterAccessibility = setter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);

                            switch (getterAccessibility)
                            {
                            case Accessibility.Public:
                            case Accessibility.ProtectedOrInternal:
                            case Accessibility.Protected:
                                // Case 4
                                getterVisible = true;
                                break;

                            case Accessibility.Internal:
                            case Accessibility.ProtectedAndInternal:
                            case Accessibility.Private:
                            default:
                                // The property is externally accessible, so the setter must be more accessible.
                                getterVisible = false;
                                break;
                            }

                            switch (setterAccessibility)
                            {
                            case Accessibility.Public:
                            case Accessibility.ProtectedOrInternal:
                            case Accessibility.Protected:
                                // Case 4
                                setterVisible = true;
                                break;

                            case Accessibility.Internal:
                            case Accessibility.ProtectedAndInternal:
                            case Accessibility.Private:
                            default:
                                // The property is externally accessible, so the getter must be more accessible.
                                setterVisible = false;
                                break;
                            }
                        }
                    }

                    if (getterVisible && !setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "get", startingTextGets));
                        }
                        else if (!text.StartsWith(startingTextGets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                        }
                    }
                    else if (!getterVisible && setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "set", startingTextSets));
                        }
                        else if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                        }
                    }
                    else
                    {
                        if (!startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGetsOrSets));
                        }
                    }
                }
                else
                {
                    if (startsWithGetOrSet || !text.StartsWith(startingTextGets, StringComparison.Ordinal))
                    {
                        diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                        context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                    }
                }
            }
            else if (setter != null)
            {
                if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                {
                    diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                    context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                }
            }
        }
示例#16
0
        private string ExpandDocumentation(Compilation compilation, DocumentationCommentTriviaSyntax documentCommentTrivia, XmlNodeSyntax includeTag)
        {
            var sb = new StringBuilder();

            sb.AppendLine("<member>");

            foreach (XmlNodeSyntax xmlNode in documentCommentTrivia.Content)
            {
                if (xmlNode == includeTag)
                {
                    this.ExpandIncludeTag(compilation, sb, xmlNode);
                }
                else
                {
                    sb.AppendLine(xmlNode.ToString());
                }
            }

            sb.AppendLine("</member>");

            return(sb.ToString());
        }
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param>
 protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, params Location[] diagnosticLocations);
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> or <c>&lt;content&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param>
 abstract protected void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, params Location[] diagnosticLocations);
        private static SyntaxList <XmlNodeSyntax> SortElements <TNode>(
            SeparatedSyntaxList <TNode> nodes,
            SyntaxList <XmlNodeSyntax> content,
            int firstIndex,
            XmlElementKind kind,
            Func <SeparatedSyntaxList <TNode>, string, int> indexOf) where TNode : SyntaxNode
        {
            var xmlNodes = new List <XmlNodeSyntax>();

            var ranks = new Dictionary <XmlNodeSyntax, int>();

            for (int i = firstIndex; i < content.Count; i++)
            {
                XmlElementInfo elementInfo = SyntaxInfo.XmlElementInfo(content[i]);

                if (elementInfo.Success)
                {
                    if (!elementInfo.IsEmptyElement &&
                        elementInfo.IsElementKind(kind))
                    {
                        var element = (XmlElementSyntax)elementInfo.Element;

                        string value = element.GetAttributeValue("name");

                        if (value != null)
                        {
                            ranks[element] = indexOf(nodes, value);
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                xmlNodes.Add(content[i]);
            }

            for (int i = 0; i < xmlNodes.Count - 1; i++)
            {
                for (int j = 0; j < xmlNodes.Count - i - 1; j++)
                {
                    XmlNodeSyntax node1 = xmlNodes[j];

                    if (ranks.TryGetValue(node1, out int rank1))
                    {
                        int k = j + 1;

                        while (k < xmlNodes.Count - i - 1)
                        {
                            XmlNodeSyntax node2 = xmlNodes[k];
                            if (ranks.TryGetValue(node2, out int rank2))
                            {
                                if (rank1 > rank2)
                                {
                                    xmlNodes[k] = node1;
                                    xmlNodes[j] = node2;
                                }

                                break;
                            }

                            k++;
                        }
                    }
                }
            }

            return(content.ReplaceRange(firstIndex, xmlNodes.Count, xmlNodes));
        }
        private async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode syntax = documentRoot.FindNode(diagnostic.Location.SourceSpan);

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

            PropertyDeclarationSyntax propertyDeclarationSyntax = syntax.FirstAncestorOrSelf <PropertyDeclarationSyntax>();

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

            DocumentationCommentTriviaSyntax documentationComment = propertyDeclarationSyntax.GetDocumentationCommentTriviaSyntax();

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

            if (!(documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) is XmlElementSyntax summaryElement))
            {
                return(document);
            }

            SyntaxList <XmlNodeSyntax> summaryContent = summaryElement.Content;

            if (!this.TryRemoveSummaryPrefix(ref summaryContent, "Gets or sets "))
            {
                if (!this.TryRemoveSummaryPrefix(ref summaryContent, "Gets "))
                {
                    this.TryRemoveSummaryPrefix(ref summaryContent, "Sets ");
                }
            }

            SyntaxList <XmlNodeSyntax> content = summaryContent.WithoutFirstAndLastNewlines();

            if (!string.IsNullOrWhiteSpace(content.ToFullString()))
            {
                // wrap the content in a <placeholder> element for review
                content = XmlSyntaxFactory.List(XmlSyntaxFactory.PlaceholderElement(content));
            }

            string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

            XmlElementSyntax valueElement = XmlSyntaxFactory.MultiLineElement(XmlCommentHelper.ValueXmlTag, newLineText, content);

            XmlNodeSyntax leadingNewLine = XmlSyntaxFactory.NewLine(newLineText);

            // HACK: The formatter isn't working when contents are added to an existing documentation comment, so we
            // manually apply the indentation from the last line of the existing comment to each new line of the
            // generated content.
            SyntaxTrivia exteriorTrivia = GetLastDocumentationCommentExteriorTrivia(documentationComment);

            if (!exteriorTrivia.Token.IsMissing)
            {
                leadingNewLine = leadingNewLine.ReplaceExteriorTrivia(exteriorTrivia);
                valueElement   = valueElement.ReplaceExteriorTrivia(exteriorTrivia);
            }

            // Try to replace an existing <value> element if the comment contains one. Otherwise, add it as a new element.
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode    newRoot;
            XmlNodeSyntax existingValue = documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.ValueXmlTag);

            if (existingValue != null)
            {
                newRoot = root.ReplaceNode(existingValue, valueElement);
            }
            else
            {
                DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent(
                    documentationComment.Content.InsertRange(
                        documentationComment.Content.Count - 1,
                        XmlSyntaxFactory.List(leadingNewLine, valueElement)));

                newRoot = root.ReplaceNode(documentationComment, newDocumentationComment);
            }

            return(document.WithSyntaxRoot(newRoot));
        }
        private bool TryRemoveSummaryPrefix(ref SyntaxList <XmlNodeSyntax> summaryContent, string prefix)
        {
            XmlNodeSyntax firstContent = summaryContent.FirstOrDefault(IsContentElement);

            if (!(firstContent is XmlTextSyntax firstText))
            {
                return(false);
            }

            string firstTextContent = string.Concat(firstText.DescendantTokens());

            if (!firstTextContent.TrimStart().StartsWith(prefix, StringComparison.Ordinal))
            {
                return(false);
            }

            // Find the token containing the prefix, such as "Gets or sets "
            SyntaxToken prefixToken = default;

            foreach (SyntaxToken textToken in firstText.TextTokens)
            {
                if (textToken.IsMissing)
                {
                    continue;
                }

                if (!textToken.Text.TrimStart().StartsWith(prefix, StringComparison.Ordinal))
                {
                    continue;
                }

                prefixToken = textToken;
                break;
            }

            if (prefixToken.IsMissingOrDefault())
            {
                return(false);
            }

            string text      = prefixToken.Text;
            string valueText = prefixToken.ValueText;
            int    index     = text.IndexOf(prefix);

            if (index >= 0)
            {
                bool additionalCharacters = index + prefix.Length < text.Length;
                text = text.Substring(0, index)
                       + (additionalCharacters ? char.ToUpperInvariant(text[index + prefix.Length]).ToString() : string.Empty)
                       + text.Substring(index + (additionalCharacters ? (prefix.Length + 1) : prefix.Length));
            }

            index = valueText.IndexOf(prefix);
            if (index >= 0)
            {
                valueText = valueText.Remove(index, prefix.Length);
            }

            SyntaxToken replaced = SyntaxFactory.Token(prefixToken.LeadingTrivia, prefixToken.Kind(), text, valueText, prefixToken.TrailingTrivia);

            summaryContent = summaryContent.Replace(firstText, firstText.ReplaceToken(prefixToken, replaced));
            return(true);
        }
        /// <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 async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode syntax = documentRoot.FindNode(diagnostic.Location.SourceSpan);

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

            MethodDeclarationSyntax   methodDeclarationSyntax   = syntax.FirstAncestorOrSelf <MethodDeclarationSyntax>();
            DelegateDeclarationSyntax delegateDeclarationSyntax = syntax.FirstAncestorOrSelf <DelegateDeclarationSyntax>();

            if (methodDeclarationSyntax == null && delegateDeclarationSyntax == null)
            {
                return(document);
            }

            DocumentationCommentTriviaSyntax documentationComment =
                methodDeclarationSyntax?.GetDocumentationCommentTriviaSyntax()
                ?? delegateDeclarationSyntax?.GetDocumentationCommentTriviaSyntax();
            bool canIgnoreDocumentation =
                documentationComment == null ||
                documentationComment.Content
                .Where(x => x is XmlElementSyntax || x is XmlEmptyElementSyntax)
                .All(x => string.Equals(x.GetName()?.ToString(), XmlCommentHelper.IncludeXmlTag));

            if (canIgnoreDocumentation)
            {
                return(document);
            }

            SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            bool isTask;
            bool isAsynchronousTestMethod;

            if (methodDeclarationSyntax != null)
            {
                isTask = TaskHelper.IsTaskReturningMethod(semanticModel, methodDeclarationSyntax, cancellationToken);
                isAsynchronousTestMethod = isTask && IsAsynchronousTestMethod(semanticModel, methodDeclarationSyntax, cancellationToken);
            }
            else
            {
                isTask = TaskHelper.IsTaskReturningMethod(semanticModel, delegateDeclarationSyntax, cancellationToken);
                isAsynchronousTestMethod = false;
            }

            XmlNodeSyntax returnsElement = documentationComment.Content.GetFirstXmlElement(XmlCommentHelper.ReturnsXmlTag);

            if (returnsElement != null && !isTask)
            {
                // This code fix doesn't know how to do anything more than document Task-returning methods.
                return(document);
            }

            SyntaxList <XmlNodeSyntax> content = XmlSyntaxFactory.List();

            if (isTask)
            {
                content = content.Add(XmlSyntaxFactory.Text("A "));
                content = content.Add(XmlSyntaxFactory.SeeElement(SyntaxFactory.TypeCref(SyntaxFactory.ParseTypeName("global::System.Threading.Tasks.Task"))).WithAdditionalAnnotations(Simplifier.Annotation));
                string operationKind = isAsynchronousTestMethod ? "unit test" : "operation";
                content = content.Add(XmlSyntaxFactory.Text($" representing the asynchronous {operationKind}."));

                // wrap the generated content in a <placeholder> element for review.
                content = XmlSyntaxFactory.List(XmlSyntaxFactory.PlaceholderElement(content));
            }

            // Try to replace an existing <returns> element if the comment contains one. Otherwise, add it as a new element.
            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            SyntaxNode newRoot;

            if (returnsElement != null)
            {
                if (returnsElement is XmlEmptyElementSyntax emptyElement)
                {
                    XmlElementSyntax updatedReturns = XmlSyntaxFactory.Element(XmlCommentHelper.ReturnsXmlTag, content)
                                                      .WithLeadingTrivia(returnsElement.GetLeadingTrivia())
                                                      .WithTrailingTrivia(returnsElement.GetTrailingTrivia());
                    newRoot = root.ReplaceNode(returnsElement, updatedReturns);
                }
                else
                {
                    XmlElementSyntax updatedReturns = ((XmlElementSyntax)returnsElement).WithContent(content);
                    newRoot = root.ReplaceNode(returnsElement, updatedReturns);
                }
            }
            else
            {
                string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);

                returnsElement = XmlSyntaxFactory.Element(XmlCommentHelper.ReturnsXmlTag, content);

                XmlNodeSyntax leadingNewLine = XmlSyntaxFactory.NewLine(newLineText);

                // HACK: The formatter isn't working when contents are added to an existing documentation comment, so we
                // manually apply the indentation from the last line of the existing comment to each new line of the
                // generated content.
                SyntaxTrivia exteriorTrivia = GetLastDocumentationCommentExteriorTrivia(documentationComment);
                if (!exteriorTrivia.Token.IsMissing)
                {
                    leadingNewLine = leadingNewLine.ReplaceExteriorTrivia(exteriorTrivia);
                    returnsElement = returnsElement.ReplaceExteriorTrivia(exteriorTrivia);
                }

                DocumentationCommentTriviaSyntax newDocumentationComment = documentationComment.WithContent(
                    documentationComment.Content.InsertRange(
                        documentationComment.Content.Count - 1,
                        XmlSyntaxFactory.List(leadingNewLine, returnsElement)));

                newRoot = root.ReplaceNode(documentationComment, newDocumentationComment);
            }

            return(document.WithSyntaxRoot(newRoot));
        }
示例#24
0
 private XmlElementInfo(XmlNodeSyntax element, string localName)
 {
     Element   = element;
     LocalName = localName;
 }
示例#25
0
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations)
        {
            if (!needsComment)
            {
                // A missing summary is allowed for this element.
                return;
            }

            if (completeDocumentation != null)
            {
                // We are working with an <include> element
                if (completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.SummaryXmlTag))
                {
                    return;
                }

                if (completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
                {
                    // Ignore nodes with an <inheritdoc/> tag in the included XML.
                    return;
                }
            }
            else
            {
                if (syntax != null)
                {
                    return;
                }

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

            foreach (var location in diagnosticLocations)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }
示例#26
0
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="needsComment"><see langword="true"/> if the current documentation settings indicate that the
 /// element should be documented; otherwise, <see langword="false"/>.</param>
 /// <param name="documentation">The documentation syntax associated with the element.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="completeDocumentation">The complete documentation for the declared symbol, with any
 /// <c>&lt;include&gt;</c> elements expanded. If the XML documentation comment included a <c>&lt;summary&gt;</c>
 /// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an
 /// <c>&lt;include&gt;</c> element.</param>
 /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param>
 protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, params Location[] diagnosticLocations);
示例#27
0
 private static XmlNameSyntax GetName(XmlNodeSyntax element)
 {
     return((element as XmlElementSyntax)?.StartTag?.Name
            ?? (element as XmlEmptyElementSyntax)?.Name);
 }
        /// <summary>
        /// This helper is used by documentation diagnostics to check if a XML comment should be considered empty.
        /// A comment is empty if it does not have any text in any XML element and it does not have an empty XML element in it.
        /// </summary>
        /// <param name="xmlSyntax">The xmlSyntax that should be checked</param>
        /// <returns>true, if the comment should be considered empty, false otherwise.</returns>
        internal static bool IsConsideredEmpty(XmlNodeSyntax xmlSyntax)
        {
            var text = xmlSyntax as XmlTextSyntax;

            if (text != null)
            {
                foreach (SyntaxToken token in text.TextTokens)
                {
                    if (!string.IsNullOrWhiteSpace(token.ToString()))
                    {
                        return(false);
                    }
                }

                return(true);
            }

            var element = xmlSyntax as XmlElementSyntax;

            if (element != null)
            {
                foreach (XmlNodeSyntax syntax in element.Content)
                {
                    if (!IsConsideredEmpty(syntax))
                    {
                        return(false);
                    }
                }

                return(true);
            }

            var cdataElement = xmlSyntax as XmlCDataSectionSyntax;

            if (cdataElement != null)
            {
                foreach (SyntaxToken token in cdataElement.TextTokens)
                {
                    if (!string.IsNullOrWhiteSpace(token.ToString()))
                    {
                        return(false);
                    }
                }

                return(true);
            }

            var emptyElement = xmlSyntax as XmlEmptyElementSyntax;

            if (emptyElement != null)
            {
                // This includes <inheritdoc/>
                return(false);
            }

            var processingElement = xmlSyntax as XmlProcessingInstructionSyntax;

            if (processingElement != null)
            {
                return(false);
            }

            return(true);
        }
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
        {
            var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
            var propertyType        = context.SemanticModel.GetTypeInfo(propertyDeclaration.Type.StripRefFromType());
            var settings            = context.Options.GetStyleCopSettings(context.CancellationToken);
            var culture             = new CultureInfo(settings.DocumentationRules.DocumentationCulture);
            var resourceManager     = DocumentationResources.ResourceManager;

            if (propertyType.Type.SpecialType == SpecialType.System_Boolean)
            {
                AnalyzeSummaryElement(
                    context,
                    syntax,
                    diagnosticLocation,
                    propertyDeclaration,
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextGetsWhether), culture),
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextSetsWhether), culture),
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextGetsOrSetsWhether), culture));
            }
            else
            {
                AnalyzeSummaryElement(
                    context,
                    syntax,
                    diagnosticLocation,
                    propertyDeclaration,
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextGets), culture),
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextSets), culture),
                    resourceManager.GetString(nameof(DocumentationResources.StartingTextGetsOrSets), culture));
            }
        }
        private static void HandleBaseTypeLikeDeclaration(SyntaxNodeAnalysisContext context)
        {
            BaseTypeDeclarationSyntax baseType = context.Node as BaseTypeDeclarationSyntax;

            // baseType can be null here if we are looking at a delegate declaration
            if (baseType != null && baseType.BaseList != null && baseType.BaseList.Types.Any())
            {
                return;
            }

            DocumentationCommentTriviaSyntax documentation = context.Node.GetDocumentationCommentTriviaSyntax();

            if (documentation == null)
            {
                return;
            }

            Location location;

            if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag) is XmlEmptyElementSyntax includeElement)
            {
                var declaration = context.SemanticModel.GetDeclaredSymbol(context.Node, context.CancellationToken);
                if (declaration == null)
                {
                    return;
                }

                var rawDocumentation      = declaration.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
                var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);

                var inheritDocElement = completeDocumentation.Nodes().OfType <XElement>().FirstOrDefault(element => element.Name == XmlCommentHelper.InheritdocXmlTag);
                if (inheritDocElement == null)
                {
                    return;
                }

                if (HasXmlCrefAttribute(inheritDocElement))
                {
                    return;
                }

                location = includeElement.GetLocation();
            }
            else
            {
                XmlNodeSyntax inheritDocElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag);
                if (inheritDocElement == null)
                {
                    return;
                }

                if (HasXmlCrefAttribute(inheritDocElement))
                {
                    return;
                }

                location = inheritDocElement.GetLocation();
            }

            context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
        }
        private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation, PropertyDeclarationSyntax propertyDeclaration, string startingTextGets, string startingTextSets, string startingTextGetsOrSets)
        {
            var diagnosticProperties = ImmutableDictionary.CreateBuilder <string, string>();
            ArrowExpressionClauseSyntax expressionBody = propertyDeclaration.ExpressionBody;
            AccessorDeclarationSyntax   getter         = null;
            AccessorDeclarationSyntax   setter         = null;

            if (propertyDeclaration.AccessorList != null)
            {
                foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
                {
                    switch (accessor.Keyword.Kind())
                    {
                    case SyntaxKind.GetKeyword:
                        getter = accessor;
                        break;

                    case SyntaxKind.SetKeyword:
                        setter = accessor;
                        break;
                    }
                }
            }

            if (!(syntax is XmlElementSyntax summaryElement))
            {
                // This is reported by SA1604 or SA1606.
                return;
            }

            // Add a no code fix tag when the summary element is empty.
            // This will only impact SA1623, because SA1624 cannot trigger with an empty summary.
            if (summaryElement.Content.Count == 0)
            {
                diagnosticProperties.Add(NoCodeFixKey, string.Empty);
            }

            var    textElement = XmlCommentHelper.TryGetFirstTextElementWithContent(summaryElement);
            string text        = textElement is null ? string.Empty : XmlCommentHelper.GetText(textElement, normalizeWhitespace: true).TrimStart();

            bool prefixIsGetsOrSets = text.StartsWith(startingTextGetsOrSets, StringComparison.OrdinalIgnoreCase);
            bool prefixIsGets       = !prefixIsGetsOrSets && text.StartsWith(startingTextGets, StringComparison.OrdinalIgnoreCase);
            bool prefixIsSets       = text.StartsWith(startingTextSets, StringComparison.OrdinalIgnoreCase);

            bool getterVisible, setterVisible;

            if (getter != null && setter != null)
            {
                if (!getter.Modifiers.Any() && !setter.Modifiers.Any())
                {
                    // The getter and setter have the same declared accessibility
                    getterVisible = true;
                    setterVisible = true;
                }
                else if (getter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                {
                    getterVisible = false;
                    setterVisible = true;
                }
                else if (setter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                {
                    getterVisible = true;
                    setterVisible = false;
                }
                else
                {
                    var  propertyAccessibility = propertyDeclaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                    bool propertyOnlyInternal  = propertyAccessibility == Accessibility.Internal ||
                                                 propertyAccessibility == Accessibility.ProtectedAndInternal ||
                                                 propertyAccessibility == Accessibility.Private;
                    if (propertyOnlyInternal)
                    {
                        // Property only internal and no accessor is explicitly private
                        getterVisible = true;
                        setterVisible = true;
                    }
                    else
                    {
                        var getterAccessibility = getter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                        var setterAccessibility = setter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);

                        switch (getterAccessibility)
                        {
                        case Accessibility.Public:
                        case Accessibility.ProtectedOrInternal:
                        case Accessibility.Protected:
                            getterVisible = true;
                            break;

                        case Accessibility.Internal:
                        case Accessibility.ProtectedAndInternal:
                        case Accessibility.Private:
                        default:
                            // The property is externally accessible, so the setter must be more accessible.
                            getterVisible = false;
                            break;
                        }

                        switch (setterAccessibility)
                        {
                        case Accessibility.Public:
                        case Accessibility.ProtectedOrInternal:
                        case Accessibility.Protected:
                            setterVisible = true;
                            break;

                        case Accessibility.Internal:
                        case Accessibility.ProtectedAndInternal:
                        case Accessibility.Private:
                        default:
                            // The property is externally accessible, so the getter must be more accessible.
                            setterVisible = false;
                            break;
                        }
                    }
                }
            }
            else
            {
                if (getter != null || expressionBody != null)
                {
                    getterVisible = true;
                    setterVisible = false;
                }
                else
                {
                    getterVisible = false;
                    setterVisible = setter != null;
                }
            }

            if (getterVisible)
            {
                if (setterVisible)
                {
                    // Both getter and setter are visible.
                    if (!prefixIsGetsOrSets)
                    {
                        ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGetsOrSets, unexpectedStartingText1: startingTextGets, unexpectedStartingText2: startingTextSets);
                    }
                }
                else if (setter != null)
                {
                    // Both getter and setter exist, but only getter is visible.
                    if (!prefixIsGets)
                    {
                        if (prefixIsGetsOrSets)
                        {
                            ReportSA1624(context, diagnosticLocation, diagnosticProperties, accessor: "get", expectedStartingText: startingTextGets, startingTextToRemove: startingTextGetsOrSets);
                        }
                        else
                        {
                            ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGets, unexpectedStartingText1: startingTextSets);
                        }
                    }
                }
                else
                {
                    // Getter exists and is visible. Setter does not exist.
                    if (!prefixIsGets)
                    {
                        ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextGets, unexpectedStartingText1: startingTextSets, unexpectedStartingText2: startingTextGetsOrSets);
                    }
                }
            }
            else if (setterVisible)
            {
                if (getter != null)
                {
                    // Both getter and setter exist, but only setter is visible.
                    if (!prefixIsSets)
                    {
                        if (prefixIsGetsOrSets)
                        {
                            ReportSA1624(context, diagnosticLocation, diagnosticProperties, accessor: "set", expectedStartingText: startingTextSets, startingTextToRemove: startingTextGetsOrSets);
                        }
                        else
                        {
                            ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextSets, unexpectedStartingText1: startingTextGets);
                        }
                    }
                }
                else
                {
                    // Setter exists and is visible. Getter does not exist.
                    if (!prefixIsSets)
                    {
                        ReportSA1623(context, diagnosticLocation, diagnosticProperties, text, expectedStartingText: startingTextSets, unexpectedStartingText1: startingTextGetsOrSets, unexpectedStartingText2: startingTextGets);
                    }
                }
            }
        }
        private static bool HasXmlCrefAttribute(XmlNodeSyntax inheritDocElement)
        {
            XmlElementSyntax xmlElementSyntax = inheritDocElement as XmlElementSyntax;
            if (xmlElementSyntax?.StartTag?.Attributes.Any(SyntaxKind.XmlCrefAttribute) ?? false)
            {
                return true;
            }

            XmlEmptyElementSyntax xmlEmptyElementSyntax = inheritDocElement as XmlEmptyElementSyntax;
            if (xmlEmptyElementSyntax?.Attributes.Any(SyntaxKind.XmlCrefAttribute) ?? false)
            {
                return true;
            }

            return false;
        }
示例#33
0
        /// <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));
            }
        }
        /// <summary>
        /// Add documentation for the property.
        /// </summary>
        /// <param name="context">the code fix context.</param>
        /// <param name="root">the root syntax node.</param>
        /// <param name="methodDeclaration">the property declaration containing invalid documentation.</param>
        /// <param name="documentComment">the existing comment.</param>
        /// <returns>the correct code.</returns>
        private Task<Document> AddDocumentationAsync(
            CodeFixContext context,
            SyntaxNode root,
            MethodDeclarationSyntax methodDeclaration,
            DocumentationCommentTriviaSyntax documentComment)
        {
            var summary = this._commentNodeFactory.GetExistingSummaryCommentText(documentComment)
                          ?? this._commentNodeFactory.CreateCommentSummaryText(methodDeclaration);
            var @class = methodDeclaration.Parent as ClassDeclarationSyntax;
            var first = @class?.DescendantNodes().FirstOrDefault() == methodDeclaration;

            var parameters = this._commentNodeFactory.CreateParameters(methodDeclaration, documentComment);
            var typeParameters = this._commentNodeFactory.CreateTypeParameters(methodDeclaration, documentComment);

            var @return = this._commentNodeFactory.CreateReturnValueDocumentation(methodDeclaration, documentComment);
            var returns = @return == null
                ? new XmlElementSyntax[] { }
                : new[] { @return };
            var summaryPlusParameters = new XmlNodeSyntax[] { summary }
                .Concat(parameters)
                .Concat(returns)
                .Concat(typeParameters)
                .ToArray();

            var comment = this._commentNodeFactory
                .CreateDocumentComment(summaryPlusParameters)
                .AddLeadingEndOfLineTriviaFrom(methodDeclaration.GetLeadingTrivia());

            var trivia = SyntaxFactory.Trivia(comment);
            var methodTrivia = first
                   ? methodDeclaration.WithLeadingTrivia(trivia)
                   : methodDeclaration.WithLeadingTrivia(SyntaxFactory.CarriageReturnLineFeed, trivia);
            var result = documentComment != null
                ? root.ReplaceNode(documentComment, comment.AdjustDocumentationCommentNewLineTrivia())
                : root.ReplaceNode(methodDeclaration, methodTrivia);

            var newDocument = context.Document.WithSyntaxRoot(result);
            return Task.FromResult(newDocument);
        }
        /// <summary>
        /// add documentation for the constructor.
        /// </summary>
        /// <param name="context">the code fix context.</param>
        /// <param name="root">the syntax root.</param>
        /// <param name="constructorDeclaration">the constructor declaration syntax.</param>
        /// <param name="documentComment">the document content.</param>
        /// <returns>the resulting document.</returns>
        private Task<Document> AddDocumentationAsync(CodeFixContext context, SyntaxNode root, ConstructorDeclarationSyntax constructorDeclaration, DocumentationCommentTriviaSyntax documentComment)
        {
            var lines = documentComment.GetExistingSummaryCommentDocumentation() ?? new string[] { };
            var standardCommentText = this._commentNodeFactory.PrependStandardCommentText(constructorDeclaration, lines);

            var parameters = this._commentNodeFactory.CreateParameters(constructorDeclaration, documentComment);

            var summaryPlusParameters = new XmlNodeSyntax[] { standardCommentText }
                .Concat(parameters)
                .ToArray();

            var comment = this._commentNodeFactory
                .CreateDocumentComment(summaryPlusParameters)
                .AddLeadingEndOfLineTriviaFrom(constructorDeclaration.GetLeadingTrivia());

            var trivia = SyntaxFactory.Trivia(comment);
            var result = documentComment != null
                ? root.ReplaceNode(documentComment, comment.AdjustDocumentationCommentNewLineTrivia())
                : root.ReplaceNode(constructorDeclaration, constructorDeclaration.WithLeadingTrivia(trivia));

            var newDocument = context.Document.WithSyntaxRoot(result);
            return Task.FromResult(newDocument);
        }
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="documentation">The documentation syntax associated with the element.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="completeDocumentation">The complete documentation for the declared symbol, with any
 /// <c>&lt;include&gt;</c> elements expanded. If the XML documentation comment included a <c>&lt;summary&gt;</c>
 /// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an
 /// <c>&lt;include&gt;</c> element.</param>
 /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param>
 protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, DocumentationCommentTriviaSyntax documentation, XmlNodeSyntax syntax, XElement completeDocumentation, params Location[] diagnosticLocations);
 private static XmlNameSyntax GetName(XmlNodeSyntax element)
 {
     return (element as XmlElementSyntax)?.StartTag?.Name
         ?? (element as XmlEmptyElementSyntax)?.Name;
 }
示例#38
0
 public static XmlElementInfo XmlElementInfo(XmlNodeSyntax xmlNode)
 {
     return(Syntax.XmlElementInfo.Create(xmlNode));
 }
示例#39
0
 public TameXmlNodeSyntax(XmlNodeSyntax node)
 {
     Node = node;
     AddChildren();
 }
        private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation, PropertyDeclarationSyntax propertyDeclaration, string startingTextGets, string startingTextSets, string startingTextGetsOrSets)
        {
            var diagnosticProperties = ImmutableDictionary.CreateBuilder<string, string>();
            ArrowExpressionClauseSyntax expressionBody = propertyDeclaration.ExpressionBody;
            AccessorDeclarationSyntax getter = null;
            AccessorDeclarationSyntax setter = null;

            if (propertyDeclaration.AccessorList != null)
            {
                foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
                {
                    switch (accessor.Keyword.Kind())
                    {
                    case SyntaxKind.GetKeyword:
                        getter = accessor;
                        break;

                    case SyntaxKind.SetKeyword:
                        setter = accessor;
                        break;
                    }
                }
            }

            XmlElementSyntax summaryElement = (XmlElementSyntax)syntax;
            if (summaryElement == null)
            {
                // This is reported by SA1604.
                return;
            }

            var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax;
            var text = textElement == null ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart();

            if (getter != null || expressionBody != null)
            {
                bool startsWithGetOrSet = text.StartsWith(startingTextGetsOrSets, StringComparison.Ordinal);

                if (setter != null)
                {
                    // There is no way getter is null (can't have expression body and accessor list)
                    var getterAccessibility = getter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                    var setterAccessibility = setter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                    bool getterVisible;
                    bool setterVisible;

                    switch (getterAccessibility)
                    {
                    case Accessibility.Public:
                    case Accessibility.ProtectedOrInternal:
                    case Accessibility.Protected:
                        getterVisible = true;
                        break;

                    case Accessibility.Internal:
                        getterVisible = setterAccessibility == Accessibility.Private;
                        break;

                    default:
                        getterVisible = false;
                        break;
                    }

                    switch (setterAccessibility)
                    {
                    case Accessibility.Public:
                    case Accessibility.ProtectedOrInternal:
                    case Accessibility.Protected:
                        setterVisible = true;
                        break;

                    case Accessibility.Internal:
                        setterVisible = getterAccessibility == Accessibility.Private;
                        break;

                    default:
                        setterVisible = false;
                        break;
                    }

                    if (getterVisible && !setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "get", startingTextGets));
                        }
                        else if (!text.StartsWith(startingTextGets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                        }
                    }
                    else if (!getterVisible && setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "set", startingTextSets));
                        }
                        else if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                        }
                    }
                    else
                    {
                        if (!startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGetsOrSets));
                        }
                    }
                }
                else
                {
                    if (startsWithGetOrSet || !text.StartsWith(startingTextGets, StringComparison.Ordinal))
                    {
                        diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                        context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                    }
                }
            }
            else if (setter != null)
            {
                if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                {
                    diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                    context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                }
            }
        }
 public void Accept(XmlNodeSyntax syntaxNode)
 {
     _state = _state.Accept(syntaxNode);
 }
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="needsComment"><see langword="true"/> if the current documentation settings indicate that the
 /// element should be documented; otherwise, <see langword="false"/>.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="completeDocumentation">The complete documentation for the declared symbol, with any
 /// <c>&lt;include&gt;</c> elements expanded. If the XML documentation comment included a <c>&lt;summary&gt;</c>
 /// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an
 /// <c>&lt;include&gt;</c> element.</param>
 /// <param name="diagnosticLocation">The location where diagnostics, if any, should be reported.</param>
 protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation);
        private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation, PropertyDeclarationSyntax propertyDeclaration, string startingTextGets, string startingTextSets, string startingTextGetsOrSets)
        {
            var diagnosticProperties = ImmutableDictionary.CreateBuilder <string, string>();
            ArrowExpressionClauseSyntax expressionBody = propertyDeclaration.ExpressionBody;
            AccessorDeclarationSyntax   getter         = null;
            AccessorDeclarationSyntax   setter         = null;

            if (propertyDeclaration.AccessorList != null)
            {
                foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
                {
                    switch (accessor.Keyword.Kind())
                    {
                    case SyntaxKind.GetKeyword:
                        getter = accessor;
                        break;

                    case SyntaxKind.SetKeyword:
                        setter = accessor;
                        break;
                    }
                }
            }

            XmlElementSyntax summaryElement = (XmlElementSyntax)syntax;

            if (summaryElement == null)
            {
                // This is reported by SA1604.
                return;
            }

            // Add a no code fix tag when the summary element is empty.
            // This will only impact SA1623, because SA1624 cannot trigger with an empty summary.
            if (summaryElement.Content.Count == 0)
            {
                diagnosticProperties.Add(NoCodeFixKey, string.Empty);
            }

            var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax;
            var text        = textElement == null ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart();

            if (getter != null || expressionBody != null)
            {
                bool startsWithGetOrSet = text.StartsWith(startingTextGetsOrSets, StringComparison.Ordinal);

                if (setter != null)
                {
                    // There is no way getter is null (can't have expression body and accessor list)
                    bool getterVisible;
                    bool setterVisible;

                    if (!getter.Modifiers.Any() && !setter.Modifiers.Any())
                    {
                        // Case 1: The getter and setter have the same declared accessibility
                        getterVisible = true;
                        setterVisible = true;
                    }
                    else if (getter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                    {
                        // Case 3
                        getterVisible = false;
                        setterVisible = true;
                    }
                    else if (setter.Modifiers.Any(SyntaxKind.PrivateKeyword))
                    {
                        // Case 3
                        getterVisible = true;
                        setterVisible = false;
                    }
                    else
                    {
                        var  propertyAccessibility = propertyDeclaration.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                        bool propertyOnlyInternal  = propertyAccessibility == Accessibility.Internal ||
                                                     propertyAccessibility == Accessibility.ProtectedAndInternal ||
                                                     propertyAccessibility == Accessibility.Private;
                        if (propertyOnlyInternal)
                        {
                            // Case 2: Property only internal and no accessor is explicitly private
                            getterVisible = true;
                            setterVisible = true;
                        }
                        else
                        {
                            var getterAccessibility = getter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);
                            var setterAccessibility = setter.GetEffectiveAccessibility(context.SemanticModel, context.CancellationToken);

                            switch (getterAccessibility)
                            {
                            case Accessibility.Public:
                            case Accessibility.ProtectedOrInternal:
                            case Accessibility.Protected:
                                // Case 4
                                getterVisible = true;
                                break;

                            case Accessibility.Internal:
                            case Accessibility.ProtectedAndInternal:
                            case Accessibility.Private:
                            default:
                                // The property is externally accessible, so the setter must be more accessible.
                                getterVisible = false;
                                break;
                            }

                            switch (setterAccessibility)
                            {
                            case Accessibility.Public:
                            case Accessibility.ProtectedOrInternal:
                            case Accessibility.Protected:
                                // Case 4
                                setterVisible = true;
                                break;

                            case Accessibility.Internal:
                            case Accessibility.ProtectedAndInternal:
                            case Accessibility.Private:
                            default:
                                // The property is externally accessible, so the getter must be more accessible.
                                setterVisible = false;
                                break;
                            }
                        }
                    }

                    if (getterVisible && !setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "get", startingTextGets));
                        }
                        else if (!text.StartsWith(startingTextGets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                        }
                    }
                    else if (!getterVisible && setterVisible)
                    {
                        if (startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            diagnosticProperties.Add(TextToRemoveKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1624Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), "set", startingTextSets));
                        }
                        else if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                        }
                    }
                    else
                    {
                        if (!startsWithGetOrSet)
                        {
                            diagnosticProperties.Add(ExpectedTextKey, startingTextGetsOrSets);
                            context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGetsOrSets));
                        }
                    }
                }
                else
                {
                    if (startsWithGetOrSet || !text.StartsWith(startingTextGets, StringComparison.Ordinal))
                    {
                        diagnosticProperties.Add(ExpectedTextKey, startingTextGets);
                        context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextGets));
                    }
                }
            }
            else if (setter != null)
            {
                if (!text.StartsWith(startingTextSets, StringComparison.Ordinal))
                {
                    diagnosticProperties.Add(ExpectedTextKey, startingTextSets);
                    context.ReportDiagnostic(Diagnostic.Create(SA1623Descriptor, diagnosticLocation, diagnosticProperties.ToImmutable(), startingTextSets));
                }
            }
        }
 /// <summary>
 /// Analyzes the top-level <c>&lt;summary&gt;</c> or <c>&lt;content&gt;</c> element of a documentation comment.
 /// </summary>
 /// <param name="context">The current analysis context.</param>
 /// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
 /// to examine.</param>
 /// <param name="completeDocumentation">The complete documentation for the declared symbol, with any
 /// <c>&lt;include&gt;</c> elements expanded. If the XML documentation comment included a <c>&lt;summary&gt;</c>
 /// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an
 /// <c>&lt;include&gt;</c> element.</param>
 /// <param name="diagnosticLocations">The location(s) where diagnostics, if any, should be reported.</param>
 protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, XElement completeDocumentation, params Location[] diagnosticLocations);
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location[] diagnosticLocations)
        {
            if (!needsComment)
            {
                // A missing summary is allowed for this element.
                return;
            }

            if (completeDocumentation != null)
            {
                var hasSummaryTag = completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.SummaryXmlTag);
                var hasContentTag = completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.ContentXmlTag);

                if (hasSummaryTag || hasContentTag)
                {
                    return;
                }
            }
            else
            {
                if (syntax != null)
                {
                    return;
                }
            }

            foreach (var location in diagnosticLocations)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }
示例#46
0
        /// <inheritdoc/>
        protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool needsComment, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
        {
            if (!needsComment)
            {
                // A missing 'value' documentation is allowed for this element.
                return;
            }

            var properties = ImmutableDictionary.Create <string, string>();

            if (completeDocumentation != null)
            {
                var hasValueTag = completeDocumentation.Nodes().OfType <XElement>().Any(element => element.Name == XmlCommentHelper.ValueXmlTag);
                if (hasValueTag)
                {
                    return;
                }

                properties = properties.Add(NoCodeFixKey, string.Empty);
            }
            else
            {
                if (syntax != null)
                {
                    return;
                }
            }

            context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation, properties));
        }
示例#47
0
 private string GetElementName(XmlNodeSyntax node) => GetElementNameAndAttributes(node).name;
        private static void HandleElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax element, ImmutableArray<string> parentTypeParameters, int index, Location alternativeDiagnosticLocation)
        {
            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))
            {
                return;
            }

            if (!parentTypeParameters.Contains(nameAttribute.Identifier.Identifier.ValueText))
            {
                context.ReportDiagnostic(Diagnostic.Create(MissingTypeParameterDescriptor, nameAttribute?.Identifier?.GetLocation() ?? alternativeDiagnosticLocation, nameAttribute.Identifier.Identifier.ValueText));
            }
            else if (parentTypeParameters.Length <= index || parentTypeParameters[index] != nameAttribute.Identifier.Identifier.ValueText)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(
                        OrderDescriptor,
                        nameAttribute?.Identifier?.GetLocation() ?? alternativeDiagnosticLocation,
                        nameAttribute.Identifier.Identifier.ValueText,
                        parentTypeParameters.IndexOf(nameAttribute.Identifier.Identifier.ValueText) + 1));
            }
        }
 public IncompatibleException(ITypeSymbol type, CrefSyntax cref)
 {
     Type   = type;
     Syntax = cref;
 }