예제 #1
0
        private static void HandleSingleLineComment(SyntaxTreeAnalysisContext context, SyntaxTrivia singleLineComment)
        {
            int index = 0;

            // PERF: Explicitly cast to IReadOnlyList so we only box once.
            IReadOnlyList <SyntaxTrivia> list = TriviaHelper.GetContainingTriviaList(singleLineComment, out index);
            var firstNonWhiteSpace            = TriviaHelper.IndexOfFirstNonWhitespaceTrivia(list);

            // When we encounter a block of single line comments, we only want to raise this diagnostic
            // on the first or last line.  This ensures that whitespace in code commented out using
            // the Comment Selection option in Visual Studio will not raise the diagnostic for every
            // blank line in the code which is commented out.
            bool isFirst = index == firstNonWhiteSpace;

            if (!isFirst)
            {
                // This is -2 because we need to go back past the end of line trivia as well.
                var lastNonWhiteSpace = TriviaHelper.IndexOfTrailingWhitespace(list) - 2;
                if (index != lastNonWhiteSpace)
                {
                    return;
                }
            }

            if (IsNullOrWhiteSpace(singleLineComment.ToString(), 2))
            {
                var diagnostic = Diagnostic.Create(Descriptor, singleLineComment.GetLocation());
                context.ReportDiagnostic(diagnostic);
            }
        }
예제 #2
0
        // If you want a full implementation of this analyzer with system tests and a code fix, go to
        // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1120CommentsMustContainText.cs

        private void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
        {
            SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken);

            foreach (var node in root.DescendantTrivia())
            {
                switch (node.Kind())
                {
                case SyntaxKind.SingleLineCommentTrivia:
                    // Remove the leading // from the comment
                    var commentText = node.ToString().Substring(2);
                    int index       = 0;

                    var  list    = TriviaHelper.GetContainingTriviaList(node, out index);
                    bool isFirst = IsFirstComment(list, index);
                    bool isLast  = IsLastComment(list, index);

                    if (string.IsNullOrWhiteSpace(commentText) && (isFirst || isLast))
                    {
                        var diagnostic = Diagnostic.Create(Rule, node.GetLocation());
                        context.ReportDiagnostic(diagnostic);
                    }

                    break;
                }
            }
        }
        private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
        {
            var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);

            foreach (var trivia in syntaxRoot.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)))
            {
                if (trivia.FullSpan.Start == 0)
                {
                    // skip the trivia if it is at the start of the file
                    continue;
                }

                if (trivia.ToString().StartsWith("////", StringComparison.Ordinal))
                {
                    // ignore commented out code
                    continue;
                }

                int triviaIndex;

                // PERF: Explicitly cast to IReadOnlyList so we only box once.
                var triviaList = TriviaHelper.GetContainingTriviaList(trivia, out triviaIndex);

                if (!IsOnOwnLine(triviaList, triviaIndex))
                {
                    // ignore comments after other code elements.
                    continue;
                }

                if (IsPrecededByBlankLine(triviaList, triviaIndex))
                {
                    // allow properly formatted blank line comments.
                    continue;
                }

                if (IsPrecededBySingleLineCommentOrDocumentation(triviaList, triviaIndex))
                {
                    // allow consecutive single line comments.
                    continue;
                }

                if (IsAtStartOfScope(trivia))
                {
                    // allow single line comment at scope start.
                    continue;
                }

                if (IsPrecededByDirectiveTrivia(triviaList, triviaIndex))
                {
                    // allow single line comment that is preceded by some directive trivia (if, elif, else)
                    continue;
                }

                var diagnosticSpan = TextSpan.FromBounds(trivia.SpanStart, trivia.SpanStart + 2);
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, diagnosticSpan)));
            }
        }
예제 #4
0
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var node = root.FindTrivia(diagnostic.Location.SourceSpan.Start, true);

            int diagnosticIndex = 0;
            var triviaList      = TriviaHelper.GetContainingTriviaList(node, out diagnosticIndex);

            var nodesToRemove = new List <SyntaxTrivia>();

            nodesToRemove.Add(node);

            // If there is trialing content on the line, we don't want to remove the leading whitespace
            bool hasTrailingContent = TriviaHasTrailingContentOnLine(root, triviaList);

            if (diagnosticIndex > 0 && !hasTrailingContent)
            {
                var previousStart = triviaList[diagnosticIndex - 1].SpanStart;
                var previousNode  = root.FindTrivia(previousStart, true);
                nodesToRemove.Add(previousNode);
            }

            // If there is leading content on the line, then we don't want to remove the trailing end of lines
            bool hasLeadingContent = TriviaHasLeadingContentOnLine(root, triviaList);

            if (diagnosticIndex < triviaList.Count - 1)
            {
                var nextStart = triviaList[diagnosticIndex + 1].SpanStart;
                var nextNode  = root.FindTrivia(nextStart, true);

                if (nextNode.IsKind(SyntaxKind.EndOfLineTrivia) && !hasLeadingContent)
                {
                    nodesToRemove.Add(nextNode);
                }
            }

            // Replace all roots with an empty node
            var newRoot = root.ReplaceTrivia(nodesToRemove, (original, rewritten) =>
            {
                return(new SyntaxTrivia());
            });

            newRoot = newRoot.NormalizeWhitespace();

            Document updatedDocument = document.WithSyntaxRoot(newRoot);

            return(updatedDocument);
        }
예제 #5
0
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var trivia = root.FindTrivia(diagnostic.Location.SourceSpan.Start, true);

            int diagnosticIndex = 0;
            var triviaList      = TriviaHelper.GetContainingTriviaList(trivia, out diagnosticIndex);

            var triviaToRemove = new List <SyntaxTrivia>();

            triviaToRemove.Add(trivia);

            bool hasTrailingContent = TriviaHasTrailingContentOnLine(root, trivia);

            if (!hasTrailingContent && diagnosticIndex > 0)
            {
                var previousTrivia = triviaList[diagnosticIndex - 1];
                if (previousTrivia.IsKind(SyntaxKind.WhitespaceTrivia))
                {
                    triviaToRemove.Add(previousTrivia);
                }
            }

            bool hasLeadingContent = TriviaHasLeadingContentOnLine(root, trivia);

            if (!hasLeadingContent && diagnosticIndex < triviaList.Count - 1)
            {
                var nextTrivia = triviaList[diagnosticIndex + 1];
                if (nextTrivia.IsKind(SyntaxKind.EndOfLineTrivia))
                {
                    triviaToRemove.Add(nextTrivia);
                }
            }

            // Replace all roots with an empty node
            var newRoot = root.ReplaceTrivia(triviaToRemove, (original, rewritten) =>
            {
                return(default(SyntaxTrivia));
            });

            Document updatedDocument = document.WithSyntaxRoot(newRoot);

            return(updatedDocument);
        }
예제 #6
0
        private static void HandleSyntaxTreeAnalysis(SyntaxTreeAnalysisContext context, ImmutableDictionary <string, ReportDiagnostic> specificDiagnosticOptions)
        {
            var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);

            foreach (var trivia in syntaxRoot.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)))
            {
                if (trivia.ToString().StartsWith("////", StringComparison.Ordinal))
                {
                    // ignore commented out code
                    continue;
                }

                int triviaIndex;

                // PERF: Explicitly cast to IReadOnlyList so we only box once.
                var triviaList = TriviaHelper.GetContainingTriviaList(trivia, out triviaIndex);

                if (!IsOnOwnLine(triviaList, triviaIndex))
                {
                    // ignore comments after other code elements.
                    continue;
                }

                if (IsPartOfFileHeader(triviaList, triviaIndex))
                {
                    // ignore comments that are part of the file header.
                    continue;
                }

                var trailingBlankLineCount = GetTrailingBlankLineCount(triviaList, ref triviaIndex);
                if (trailingBlankLineCount == 0)
                {
                    // ignore comments that are not followed by a blank line
                    continue;
                }
                else if (trailingBlankLineCount > 1)
                {
                    if (specificDiagnosticOptions.GetValueOrDefault(SA1507CodeMustNotContainMultipleBlankLinesInARow.DiagnosticId, ReportDiagnostic.Default) != ReportDiagnostic.Suppress)
                    {
                        // ignore comments that are followed by multiple blank lines -> the multiple blank lines will be reported by SA1507
                        continue;
                    }
                }
                else
                {
                    if (triviaIndex < triviaList.Count)
                    {
                        switch (triviaList[triviaIndex].Kind())
                        {
                        case SyntaxKind.SingleLineCommentTrivia:
                        case SyntaxKind.SingleLineDocumentationCommentTrivia:
                        case SyntaxKind.MultiLineCommentTrivia:
                        case SyntaxKind.MultiLineDocumentationCommentTrivia:
                            // ignore a single blank line in between two comments.
                            continue;
                        }
                    }
                }

                var diagnosticSpan = TextSpan.FromBounds(trivia.SpanStart, trivia.SpanStart + 2);
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, diagnosticSpan)));
            }
        }
        private static void HandleDeclaration(SyntaxNodeAnalysisContext context)
        {
            var nodeTriviaList           = context.Node.GetLeadingTrivia();
            var documentationHeaderIndex = context.Node.GetLeadingTrivia().IndexOf(SyntaxKind.SingleLineDocumentationCommentTrivia);

            if (documentationHeaderIndex == -1)
            {
                // there is no documentation header.
                return;
            }

            var documentationHeader = nodeTriviaList[documentationHeaderIndex];
            var triviaList          = TriviaHelper.GetContainingTriviaList(documentationHeader, out documentationHeaderIndex);
            var eolCount            = 0;
            var done = false;

            for (var i = documentationHeaderIndex - 1; !done && (i >= 0); i--)
            {
                var trivia = triviaList[i];
                if (trivia.IsDirective &&
                    !trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia) &&
                    !trivia.IsKind(SyntaxKind.RegionDirectiveTrivia) &&
                    !trivia.IsKind(SyntaxKind.EndRegionDirectiveTrivia))
                {
                    return;
                }

                switch (trivia.Kind())
                {
                case SyntaxKind.WhitespaceTrivia:
                    break;

                case SyntaxKind.EndOfLineTrivia:
                    eolCount++;
                    break;

                case SyntaxKind.EndIfDirectiveTrivia:
                case SyntaxKind.RegionDirectiveTrivia:
                case SyntaxKind.EndRegionDirectiveTrivia:
                    eolCount++;
                    done = true;
                    break;

                default:
                    done = true;
                    break;
                }
            }

            if (eolCount >= 2)
            {
                // there is a blank line available
                return;
            }

            if (!done)
            {
                var prevToken = documentationHeader.Token.GetPreviousToken();
                if (prevToken.IsKind(SyntaxKind.OpenBraceToken))
                {
                    // no leading blank line necessary at start of scope.
                    return;
                }
            }

            context.ReportDiagnostic(Diagnostic.Create(Descriptor, GetDiagnosticLocation(documentationHeader)));
        }