private static BlockSyntax ReformatBodyAsMultipleLines(BlockSyntax body, SyntaxTrivia indentation, SyntaxTrivia indentationStatements)
        {
            SyntaxTriviaList reformattedOpenBraceTrailingTrivia;
            SyntaxTriviaList reformattedCloseBraceLeadingTrivia;

            SyntaxList <StatementSyntax> newStatements;

            if (body.Statements.Count > 0)
            {
                var reformattedStatement = body.Statements[0]
                                           .WithLeadingTrivia(ReformatTriviaListNoLeadingSpace(body.Statements[0].GetLeadingTrivia()).Insert(0, indentationStatements))
                                           .WithTrailingTrivia(ReformatTriviaListNoTrailingSpace(body.Statements[0].GetTrailingTrivia()).Add(SyntaxFactory.CarriageReturnLineFeed));

                reformattedOpenBraceTrailingTrivia = ReformatTriviaListNoTrailingSpace(body.OpenBraceToken.TrailingTrivia);
                newStatements = SyntaxFactory.List <StatementSyntax>().Add(reformattedStatement);
                reformattedCloseBraceLeadingTrivia = ReformatTriviaListNoLeadingSpace(body.CloseBraceToken.LeadingTrivia);
            }
            else
            {
                var triviaBetweenBraces = TriviaHelper.MergeTriviaLists(body.OpenBraceToken.TrailingTrivia, body.CloseBraceToken.LeadingTrivia);
                reformattedOpenBraceTrailingTrivia = ReformatTriviaListNoTrailingSpace(triviaBetweenBraces);
                newStatements = body.Statements;
                reformattedCloseBraceLeadingTrivia = SyntaxFactory.TriviaList();
            }

            var newOpenBraceToken = body.OpenBraceToken
                                    .WithLeadingTrivia(ReformatTriviaListNoLeadingSpace(body.OpenBraceToken.LeadingTrivia).Insert(0, indentation))
                                    .WithTrailingTrivia(reformattedOpenBraceTrailingTrivia.Add(SyntaxFactory.CarriageReturnLineFeed));

            var newCloseBraceToken = body.CloseBraceToken
                                     .WithLeadingTrivia(reformattedCloseBraceLeadingTrivia.Insert(0, indentation))
                                     .WithTrailingTrivia(ReformatTriviaListNoTrailingSpace(body.CloseBraceToken.TrailingTrivia).Add(SyntaxFactory.CarriageReturnLineFeed));

            return(body.Update(newOpenBraceToken, newStatements, newCloseBraceToken));
        }
        private static void HandleUsings(SyntaxNodeAnalysisContext context, SyntaxList <UsingDirectiveSyntax> usings, StyleCopSettings settings)
        {
            if (usings.Count < 2)
            {
                return;
            }

            var blankLinesBetweenUsingGroups = settings.OrderingRules.BlankLinesBetweenUsingGroups;

            var previousGroupType = usings[0].GetUsingGroupType(settings);
            var previousLineSpan  = usings[0].GetLineSpan();

            for (var i = 1; i < usings.Count; i++)
            {
                var currentGroupType = usings[i].GetUsingGroupType(settings);
                var currentLineSpan  = usings[i].GetLineSpan();

                var partOfSameGroup = previousGroupType == currentGroupType;
                var lineDistance    = currentLineSpan.StartLinePosition.Line - previousLineSpan.EndLinePosition.Line;

                previousGroupType = currentGroupType;
                previousLineSpan  = currentLineSpan;

                if (partOfSameGroup)
                {
                    // if the using statements are part of the same group, there is no need to check.
                    continue;
                }

                if (blankLinesBetweenUsingGroups == OptionSetting.Require)
                {
                    if (lineDistance > 1)
                    {
                        var separatingTrivia = TriviaHelper.MergeTriviaLists(usings[i - 1].GetTrailingTrivia(), usings[i].GetLeadingTrivia());
                        if (separatingTrivia.ContainsBlankLines(false))
                        {
                            continue;
                        }
                    }

                    context.ReportDiagnostic(Diagnostic.Create(DescriptorRequire, usings[i].UsingKeyword.GetLocation(), DiagnosticPropertiesRequire));
                }
                else if (blankLinesBetweenUsingGroups == OptionSetting.Omit)
                {
                    if (lineDistance < 2)
                    {
                        // no point in checking the trivia if the using statements are not separated.
                        continue;
                    }

                    var separatingTrivia = TriviaHelper.MergeTriviaLists(usings[i - 1].GetTrailingTrivia(), usings[i].GetLeadingTrivia());
                    if (!separatingTrivia.ContainsBlankLines(false))
                    {
                        continue;
                    }

                    context.ReportDiagnostic(Diagnostic.Create(DescriptorOmit, usings[i].UsingKeyword.GetLocation(), DiagnosticPropertiesOmit));
                }
            }
        }
        private static void HandleNullableType(SyntaxNodeAnalysisContext context)
        {
            var nullableType  = (NullableTypeSyntax)context.Node;
            var questionToken = nullableType.QuestionToken;

            if (questionToken.IsMissing)
            {
                return;
            }

            if (nullableType.ElementType.IsMissing)
            {
                return;
            }

            /* Do not test for the first character on the line!
             * The StyleCop documentation is wrong there, the actual StyleCop code does not accept it.
             */

            SyntaxToken precedingToken = questionToken.GetPreviousToken();
            var         triviaList     = TriviaHelper.MergeTriviaLists(precedingToken.TrailingTrivia, questionToken.LeadingTrivia);

            if (triviaList.Any(t => t.IsKind(SyntaxKind.WhitespaceTrivia) || t.IsKind(SyntaxKind.EndOfLineTrivia)))
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, questionToken.GetLocation()));
            }
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var accessorToken = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start);
            var accessorList  = (AccessorListSyntax)accessorToken.Parent.Parent;

            var firstAccesor         = accessorList.Accessors[0];
            var secondAccessor       = accessorList.Accessors[1];
            var trackedRoot          = syntaxRoot.TrackNodes(firstAccesor, secondAccessor);
            var trackedFirstAccessor = trackedRoot.GetCurrentNode(firstAccesor);
            var newAccessor          = GetNewAccessor(accessorList, firstAccesor, secondAccessor);

            syntaxRoot = trackedRoot.InsertNodesBefore(trackedFirstAccessor, new[] { newAccessor });

            trackedFirstAccessor = syntaxRoot.GetCurrentNode(firstAccesor);
            if (secondAccessor.GetFirstToken().HasLeadingBlankLines())
            {
                var newFirstAccessor = trackedFirstAccessor.WithLeadingTrivia(
                    TriviaHelper.MergeTriviaLists(new[] { SyntaxFactory.CarriageReturnLineFeed }, trackedFirstAccessor.GetLeadingTrivia()));
                syntaxRoot = syntaxRoot.ReplaceNode(trackedFirstAccessor, newFirstAccessor);
            }

            var trackedLastAccessor = syntaxRoot.GetCurrentNode(secondAccessor);
            var keepTriviaOptions   = AccessorsAreOnTheSameLine(firstAccesor, secondAccessor)
                ? SyntaxRemoveOptions.KeepEndOfLine
                : SyntaxRemoveOptions.KeepNoTrivia;

            syntaxRoot = syntaxRoot.RemoveNode(trackedLastAccessor, keepTriviaOptions);

            return(document.WithSyntaxRoot(syntaxRoot));
        }
        private static void AnalyzeCloseBrace(SyntaxNodeAnalysisContext context, SyntaxToken closeBraceToken)
        {
            if (closeBraceToken.IsKind(SyntaxKind.None))
            {
                return;
            }

            var previousToken = closeBraceToken.GetPreviousToken();

            if ((closeBraceToken.GetLineSpan().StartLinePosition.Line - previousToken.GetLineSpan().EndLinePosition.Line) < 2)
            {
                // there will be no blank lines when the closing brace and the preceding token are not at least two lines apart.
                return;
            }

            var separatingTrivia = TriviaHelper.MergeTriviaLists(previousToken.TrailingTrivia, closeBraceToken.LeadingTrivia);

            // skip all leading whitespace for the close brace
            // the index must be checked because two tokens can be more than two lines apart and
            // still only be separated by whitespace trivia due to compilation errors
            var index = separatingTrivia.Count - 1;

            while (index >= 0 && separatingTrivia[index].IsKind(SyntaxKind.WhitespaceTrivia))
            {
                index--;
            }

            var done     = false;
            var eolCount = 0;

            while (!done && index >= 0)
            {
                switch (separatingTrivia[index].Kind())
                {
                case SyntaxKind.WhitespaceTrivia:
                    break;

                case SyntaxKind.EndOfLineTrivia:
                    eolCount++;
                    break;

                default:
                    done = true;
                    break;
                }

                index--;
            }

            if (eolCount > 1)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, closeBraceToken.GetLocation()));
            }
        }
예제 #6
0
        private static void AnalyzeOpenBrace(SyntaxNodeAnalysisContext context, SyntaxToken openBraceToken)
        {
            var nextToken = openBraceToken.GetNextToken();

            if (nextToken.IsMissingOrDefault())
            {
                return;
            }

            if ((GetLine(nextToken) - GetLine(openBraceToken)) < 2)
            {
                // there will be no blank lines when the opening brace and the following token are not at least two lines apart.
                return;
            }

            var separatingTrivia = TriviaHelper.MergeTriviaLists(openBraceToken.TrailingTrivia, nextToken.LeadingTrivia);

            // skip everything until the first end of line (as this is considered part of the line of the opening brace)
            var startIndex = 0;

            while ((startIndex < separatingTrivia.Count) && !separatingTrivia[startIndex].IsKind(SyntaxKind.EndOfLineTrivia))
            {
                startIndex++;
            }

            startIndex = (startIndex == separatingTrivia.Count) ? 0 : startIndex + 1;

            var done     = false;
            var eolCount = 0;

            for (var i = startIndex; !done && (i < separatingTrivia.Count); i++)
            {
                switch (separatingTrivia[i].Kind())
                {
                case SyntaxKind.WhitespaceTrivia:
                    break;

                case SyntaxKind.EndOfLineTrivia:
                    eolCount++;
                    break;

                default:
                    done = true;
                    break;
                }
            }

            if (eolCount > 0)
            {
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, openBraceToken.GetLocation()));
            }
        }
예제 #7
0
        private static void HandlePrefixUnaryExpression(SyntaxNodeAnalysisContext context)
        {
            var unaryExpression = (PrefixUnaryExpressionSyntax)context.Node;
            var precedingToken  = unaryExpression.OperatorToken.GetPreviousToken();
            var followingToken  = unaryExpression.OperatorToken.GetNextToken();
            var followingTrivia = TriviaHelper.MergeTriviaLists(unaryExpression.OperatorToken.TrailingTrivia, followingToken.LeadingTrivia);

            /* let the outer operator handle things like the following, so no error is reported for '++':
             *   c ^= *++buf4;
             *
             * if the unary expression is inside parenthesis or an indexer, there should be no leading space
             */
            var mustHaveLeadingWhitespace = !(unaryExpression.Parent is PrefixUnaryExpressionSyntax) &&
                                            !(unaryExpression.Parent is CastExpressionSyntax) &&
                                            !precedingToken.IsKind(SyntaxKind.OpenParenToken) &&
                                            !precedingToken.IsKind(SyntaxKind.OpenBracketToken) &&
                                            !(precedingToken.IsKind(SyntaxKind.OpenBraceToken) && (precedingToken.Parent is InterpolationSyntax));

            bool analyze;

            switch (unaryExpression.OperatorToken.Kind())
            {
            case SyntaxKind.PlusToken:
                analyze = context.IsAnalyzerSuppressed(SA1022PositiveSignsMustBeSpacedCorrectly.DiagnosticId);
                break;

            case SyntaxKind.MinusToken:
                analyze = context.IsAnalyzerSuppressed(SA1021NegativeSignsMustBeSpacedCorrectly.DiagnosticId);
                break;

            case SyntaxKind.PlusPlusToken:
            case SyntaxKind.MinusMinusToken:
                analyze = context.IsAnalyzerSuppressed(SA1020IncrementDecrementSymbolsMustBeSpacedCorrectly.DiagnosticId);
                break;

            default:
                analyze = true;
                break;
            }

            if (analyze)
            {
                if (followingTrivia.Any(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia)) || followingTrivia.Any(t => t.IsKind(SyntaxKind.MultiLineCommentTrivia)))
                {
                    context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowedByComment, unaryExpression.OperatorToken.GetLocation(), unaryExpression.OperatorToken.Text));
                }
                else
                {
                    CheckToken(context, unaryExpression.OperatorToken, mustHaveLeadingWhitespace, false, false);
                }
            }
        }
        private static AccessorDeclarationSyntax GetNewAccessor(AccessorListSyntax accessorList, AccessorDeclarationSyntax firstAccessor, AccessorDeclarationSyntax secondAccessor)
        {
            var newLeadingTrivia = GetLeadingTriviaWithoutLeadingBlankLines(secondAccessor);

            if (AccessorsAreOnTheSameLine(firstAccessor, secondAccessor))
            {
                var leadingWhitespace = firstAccessor.GetLeadingTrivia().Where(x => x.IsKind(SyntaxKind.WhitespaceTrivia));
                newLeadingTrivia = SyntaxFactory.TriviaList(TriviaHelper.MergeTriviaLists(newLeadingTrivia, SyntaxTriviaList.Empty.AddRange(leadingWhitespace)));
            }

            var newAccessor = accessorList.Accessors[1]
                              .WithBody(secondAccessor.Body)
                              .WithLeadingTrivia(newLeadingTrivia);

            return(newAccessor);
        }
        private static AccessorDeclarationSyntax GetNewAccessor(AccessorListSyntax accessorList, AccessorDeclarationSyntax firstAccessor, AccessorDeclarationSyntax secondAccessor)
        {
            var newLeadingTrivia = GetLeadingTriviaWithoutLeadingBlankLines(secondAccessor);

            if (AccessorsAreOnTheSameLine(firstAccessor, secondAccessor))
            {
                var leadingWhitespace = firstAccessor.GetLeadingTrivia().Where(x => x.IsKind(SyntaxKind.WhitespaceTrivia)).ToList();
                newLeadingTrivia = SyntaxFactory.TriviaList(TriviaHelper.MergeTriviaLists(newLeadingTrivia, leadingWhitespace));
            }

            var newAccessor = accessorList.Accessors[1]
                              .WithBody(secondAccessor.Body)
                              .WithLeadingTrivia(newLeadingTrivia);

            if (secondAccessor.GetFirstToken().HasLeadingBlankLines())
            {
                newAccessor.WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.CarriageReturnLineFeed);
            }

            return(newAccessor);
        }
예제 #10
0
        private static void AnalyzeOpenBrace(SyntaxTreeAnalysisContext context, SyntaxToken openBrace)
        {
            var prevToken  = openBrace.GetPreviousToken();
            var triviaList = TriviaHelper.MergeTriviaLists(prevToken.TrailingTrivia, openBrace.LeadingTrivia);

            var done     = false;
            var eolCount = 0;

            for (var i = triviaList.Count - 1; !done && (i >= 0); i--)
            {
                switch (triviaList[i].Kind())
                {
                case SyntaxKind.WhitespaceTrivia:
                    break;

                case SyntaxKind.EndOfLineTrivia:
                    eolCount++;
                    break;

                default:
                    if (triviaList[i].IsDirective)
                    {
                        // These have a built-in end of line
                        eolCount++;
                    }

                    done = true;
                    break;
                }
            }

            if (eolCount < 2)
            {
                return;
            }

            context.ReportDiagnostic(Diagnostic.Create(Descriptor, openBrace.GetLocation()));
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, syntaxRoot.SyntaxTree, cancellationToken);

            var enumMemberDeclaration = (EnumMemberDeclarationSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan);
            var enumDeclaration       = (EnumDeclarationSyntax)enumMemberDeclaration.Parent;

            var memberIndex             = enumDeclaration.Members.IndexOf(enumMemberDeclaration);
            var precedingSeparatorToken = enumDeclaration.Members.GetSeparator(memberIndex - 1);

            // determine the indentation for enum members (which is parent + 1 step)
            var parentIndentationSteps = IndentationHelper.GetIndentationSteps(settings.Indentation, enumDeclaration);
            var indentation            = IndentationHelper.GenerateWhitespaceTrivia(settings.Indentation, parentIndentationSteps + 1);

            // combine all trivia between the separator and the enum member and place them after the separator, followed by a new line.
            var enumMemberDeclarationFirstToken = enumMemberDeclaration.GetFirstToken();
            var sharedTrivia = TriviaHelper.MergeTriviaLists(precedingSeparatorToken.TrailingTrivia, enumMemberDeclarationFirstToken.LeadingTrivia);

            var newTrailingTrivia = SyntaxFactory.TriviaList(sharedTrivia)
                                    .WithoutTrailingWhitespace()
                                    .Add(SyntaxFactory.CarriageReturnLineFeed);

            // replace the trivia for the tokens
            var replacements = new Dictionary <SyntaxToken, SyntaxToken>
            {
                [precedingSeparatorToken]         = precedingSeparatorToken.WithTrailingTrivia(newTrailingTrivia),
                [enumMemberDeclarationFirstToken] = enumMemberDeclarationFirstToken.WithLeadingTrivia(indentation),
            };

            var newSyntaxRoot = syntaxRoot.ReplaceTokens(replacements.Keys, (original, rewritten) => replacements[original]);
            var newDocument   = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

            return(newDocument);
        }
예제 #12
0
        private static void CheckToken(SyntaxNodeAnalysisContext context, SyntaxToken token, bool withLeadingWhitespace, bool allowAtEndOfLine, bool withTrailingWhitespace, string tokenText = null)
        {
            tokenText = tokenText ?? token.Text;

            var precedingToken      = token.GetPreviousToken();
            var precedingTriviaList = TriviaHelper.MergeTriviaLists(precedingToken.TrailingTrivia, token.LeadingTrivia);

            var followingToken      = token.GetNextToken();
            var followingTriviaList = TriviaHelper.MergeTriviaLists(token.TrailingTrivia, followingToken.LeadingTrivia);

            if (withLeadingWhitespace)
            {
                // Don't report missing leading whitespace when the token is the first token on a text line.
                if (!token.IsFirstInLine() &&
                    ((precedingTriviaList.Count == 0) || !precedingTriviaList.Last().IsKind(SyntaxKind.WhitespaceTrivia)))
                {
                    var properties = ImmutableDictionary.Create <string, string>()
                                     .Add(CodeFixAction, InsertBeforeTag);
                    context.ReportDiagnostic(Diagnostic.Create(DescriptorPrecededByWhitespace, token.GetLocation(), properties, tokenText));
                }
            }
            else
            {
                // don't report leading whitespace when the token is the first token on a text line
                if (!token.IsOnlyPrecededByWhitespaceInLine() &&
                    ((precedingTriviaList.Count > 0) && precedingTriviaList.Last().IsKind(SyntaxKind.WhitespaceTrivia)))
                {
                    var properties = ImmutableDictionary.Create <string, string>()
                                     .Add(CodeFixAction, RemoveBeforeTag);
                    context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPrecededByWhitespace, token.GetLocation(), properties, tokenText));
                }
            }

            if (!allowAtEndOfLine && token.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia))
            {
                var properties = ImmutableDictionary.Create <string, string>();

                // Do not register a code fix action if there are non whitespace or end of line tokens present.
                if (followingTriviaList.All(t => t.IsKind(SyntaxKind.WhitespaceTrivia) || t.IsKind(SyntaxKind.EndOfLineTrivia)))
                {
                    properties = properties.Add(CodeFixAction, withTrailingWhitespace ? RemoveEndOfLineWithTrailingSpaceTag : RemoveEndOfLineTag);
                }

                context.ReportDiagnostic(Diagnostic.Create(DescriptorNotAtEndOfLine, token.GetLocation(), properties, tokenText));
                return;
            }

            if (withTrailingWhitespace)
            {
                if ((followingTriviaList.Count == 0) || !(followingTriviaList.First().IsKind(SyntaxKind.WhitespaceTrivia) || followingTriviaList.First().IsKind(SyntaxKind.EndOfLineTrivia)))
                {
                    var properties = ImmutableDictionary.Create <string, string>()
                                     .Add(CodeFixAction, InsertAfterTag);
                    context.ReportDiagnostic(Diagnostic.Create(DescriptorFollowedByWhitespace, token.GetLocation(), properties, tokenText));
                }
            }
            else
            {
                if ((followingTriviaList.Count > 0) && followingTriviaList.First().IsKind(SyntaxKind.WhitespaceTrivia))
                {
                    var properties = ImmutableDictionary.Create <string, string>()
                                     .Add(CodeFixAction, RemoveAfterTag);
                    context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowedByWhitespace, token.GetLocation(), properties, tokenText));
                }
            }
        }
예제 #13
0
        private static void HandleOpenParenToken(SyntaxTreeAnalysisContext context, SyntaxToken token)
        {
            if (token.IsMissing)
            {
                return;
            }

            if (token.IsLastInLine())
            {
                // ignore open parenthesis when last on line.
                return;
            }

            var prevToken         = token.GetPreviousToken();
            var leadingTriviaList = TriviaHelper.MergeTriviaLists(prevToken.TrailingTrivia, token.LeadingTrivia);

            var isFirstOnLine = false;

            if (prevToken.GetLineSpan().EndLinePosition.Line < token.GetLineSpan().StartLinePosition.Line)
            {
                var done = false;
                for (var i = leadingTriviaList.Count - 1; !done && (i >= 0); i--)
                {
                    switch (leadingTriviaList[i].Kind())
                    {
                    case SyntaxKind.WhitespaceTrivia:
                        break;

                    case SyntaxKind.EndOfLineTrivia:
                        isFirstOnLine = true;
                        done          = true;
                        break;

                    default:
                        done = true;
                        break;
                    }
                }
            }

            bool haveLeadingSpace;
            bool partOfUnaryExpression;
            bool startOfIndexer;

            var prevTokenIsOpenParen = prevToken.IsKind(SyntaxKind.OpenParenToken);

            switch (token.Parent.Kind())
            {
            case SyntaxKind.IfStatement:
            case SyntaxKind.DoStatement:
            case SyntaxKind.WhileStatement:
            case SyntaxKind.ForStatement:
            case SyntaxKind.ForEachStatement:
            case SyntaxKind.SwitchStatement:
            case SyntaxKind.FixedStatement:
            case SyntaxKind.LockStatement:
            case SyntaxKind.UsingStatement:
            case SyntaxKind.CatchDeclaration:
            case SyntaxKind.CatchFilterClause:
                haveLeadingSpace = true;
                break;

            case SyntaxKind.ArgumentList:
            case SyntaxKind.AttributeArgumentList:
            case SyntaxKind.CheckedExpression:
            case SyntaxKind.UncheckedExpression:
            case SyntaxKind.ConstructorConstraint:
            case SyntaxKind.DefaultExpression:
            case SyntaxKind.SizeOfExpression:
            case SyntaxKind.TypeOfExpression:
            default:
                haveLeadingSpace = false;
                break;

            case SyntaxKind.ParenthesizedExpression:
                if (prevToken.Parent.IsKind(SyntaxKind.Interpolation))
                {
                    haveLeadingSpace = false;
                    break;
                }

                partOfUnaryExpression = prevToken.Parent is PrefixUnaryExpressionSyntax;
                startOfIndexer        = prevToken.IsKind(SyntaxKind.OpenBracketToken);
                var partOfCastExpression = prevToken.IsKind(SyntaxKind.CloseParenToken) && prevToken.Parent.IsKind(SyntaxKind.CastExpression);

                haveLeadingSpace = !partOfUnaryExpression && !startOfIndexer && !partOfCastExpression;
                break;

            case SyntaxKind.CastExpression:
                partOfUnaryExpression = prevToken.Parent is PrefixUnaryExpressionSyntax;
                startOfIndexer        = prevToken.IsKind(SyntaxKind.OpenBracketToken);
                var consecutiveCast     = prevToken.IsKind(SyntaxKind.CloseParenToken) && prevToken.Parent.IsKind(SyntaxKind.CastExpression);
                var partOfInterpolation = prevToken.IsKind(SyntaxKind.OpenBraceToken) && prevToken.Parent.IsKind(SyntaxKind.Interpolation);

                haveLeadingSpace = !partOfUnaryExpression && !startOfIndexer && !consecutiveCast && !partOfInterpolation;
                break;

            case SyntaxKind.ParameterList:
                var partOfLambdaExpression = token.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression);
                haveLeadingSpace = partOfLambdaExpression;
                break;
            }

            // Ignore spacing before if another opening parenthesis is before this.
            // That way the first opening parenthesis will report any spacing errors.
            if (!prevTokenIsOpenParen)
            {
                var hasLeadingComment = (leadingTriviaList.Count > 0) && leadingTriviaList.Last().IsKind(SyntaxKind.MultiLineCommentTrivia);
                var hasLeadingSpace   = (leadingTriviaList.Count > 0) && leadingTriviaList.Last().IsKind(SyntaxKind.WhitespaceTrivia);

                if (!isFirstOnLine && !hasLeadingComment && (haveLeadingSpace != hasLeadingSpace))
                {
                    if (haveLeadingSpace)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DescriptorPreceded, token.GetLocation(), TokenSpacingProperties.InsertPreceding));
                    }
                    else
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), TokenSpacingProperties.RemovePreceding));
                    }
                }
            }

            if (token.IsFollowedByWhitespace())
            {
                context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowed, token.GetLocation(), TokenSpacingProperties.RemoveFollowing));
            }
        }
        private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxToken token)
        {
            if (token.IsMissing)
            {
                return;
            }

            bool requireBefore;
            var  checkRequireAfter = true;

            switch (token.Parent.Kind())
            {
            case SyntaxKind.BaseList:
            case SyntaxKind.BaseConstructorInitializer:
            case SyntaxKind.ThisConstructorInitializer:
            case SyntaxKind.TypeParameterConstraintClause:
            case SyntaxKind.ConditionalExpression:
                requireBefore = true;
                break;

            case SyntaxKind.LabeledStatement:
            case SyntaxKind.CaseSwitchLabel:
            case SyntaxKind.DefaultSwitchLabel:
            case SyntaxKindEx.CasePatternSwitchLabel:
            // NameColon is not explicitly listed in the description of this warning, but the behavior is inferred
            case SyntaxKind.NameColon:
                requireBefore = false;
                break;

            case SyntaxKind.InterpolationFormatClause:
                requireBefore     = false;
                checkRequireAfter = false;
                break;

            default:
                return;
            }

            // check for a following space
            bool missingFollowingSpace = true;

            if (token.HasTrailingTrivia)
            {
                if (token.TrailingTrivia.First().IsKind(SyntaxKind.WhitespaceTrivia))
                {
                    missingFollowingSpace = false;
                }
                else if (token.TrailingTrivia.First().IsKind(SyntaxKind.EndOfLineTrivia))
                {
                    missingFollowingSpace = false;
                }
            }

            bool hasPrecedingSpace = token.HasLeadingTrivia;

            if (!hasPrecedingSpace)
            {
                // only the first token on the line has leading trivia, and those are ignored
                SyntaxToken precedingToken = token.GetPreviousToken();
                var         combinedTrivia = TriviaHelper.MergeTriviaLists(precedingToken.TrailingTrivia, token.LeadingTrivia);
                if (combinedTrivia.Count > 0 && !combinedTrivia.Last().IsKind(SyntaxKind.MultiLineCommentTrivia))
                {
                    hasPrecedingSpace = true;
                }
            }

            if (hasPrecedingSpace != requireBefore)
            {
                // colon should{ not}? be {preceded}{} by a space
                var properties = requireBefore ? TokenSpacingProperties.InsertPreceding : TokenSpacingProperties.RemovePreceding;
                context.ReportDiagnostic(Diagnostic.Create(requireBefore ? DescriptorPreceded : DescriptorNotPreceded, token.GetLocation(), properties));
            }

            if (missingFollowingSpace && checkRequireAfter)
            {
                // colon should{} be {followed}{} by a space
#pragma warning disable RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor (https://github.com/dotnet/roslyn-analyzers/issues/4103)
                context.ReportDiagnostic(Diagnostic.Create(DescriptorFollowed, token.GetLocation(), TokenSpacingProperties.InsertFollowing));
#pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor
            }
        }
예제 #15
0
        private static void HandleOpenParenToken(SyntaxTreeAnalysisContext context, SyntaxToken token)
        {
            if (token.IsMissing)
            {
                return;
            }

            var prevToken = token.GetPreviousToken();

            // Don't check leading spaces when preceded by a keyword that is already handled by SA1000
            bool precededByKeyword;

            switch (prevToken.Kind())
            {
            case SyntaxKind.AwaitKeyword:
            case SyntaxKind.CaseKeyword:
            case SyntaxKind.CatchKeyword:
            case SyntaxKind.CheckedKeyword:
            case SyntaxKind.DefaultKeyword:
            case SyntaxKind.FixedKeyword:
            case SyntaxKind.ForKeyword:
            case SyntaxKind.ForEachKeyword:
            case SyntaxKind.FromKeyword:
            case SyntaxKind.GroupKeyword:
            case SyntaxKind.IfKeyword:
            case SyntaxKind.InKeyword:
            case SyntaxKind.IntoKeyword:
            case SyntaxKind.JoinKeyword:
            case SyntaxKind.LetKeyword:
            case SyntaxKind.LockKeyword:
            case SyntaxKind.NameOfKeyword:
            case SyntaxKind.NewKeyword:
            case SyntaxKind.OrderByKeyword:
            case SyntaxKind.ReturnKeyword:
            case SyntaxKind.SelectKeyword:
            case SyntaxKind.SizeOfKeyword:
            case SyntaxKind.StackAllocKeyword:
            case SyntaxKind.SwitchKeyword:
            case SyntaxKind.ThrowKeyword:
            case SyntaxKind.TypeOfKeyword:
            case SyntaxKind.UncheckedKeyword:
            case SyntaxKind.UsingKeyword:
            case SyntaxKind.WhereKeyword:
            case SyntaxKind.WhileKeyword:
            case SyntaxKind.YieldKeyword:
                precededByKeyword = true;
                break;

            default:
                precededByKeyword = false;
                break;
            }

            var leadingTriviaList = TriviaHelper.MergeTriviaLists(prevToken.TrailingTrivia, token.LeadingTrivia);

            var isFirstOnLine = false;

            if (prevToken.GetLineSpan().EndLinePosition.Line < token.GetLineSpan().StartLinePosition.Line)
            {
                var done = false;
                for (var i = leadingTriviaList.Count - 1; !done && (i >= 0); i--)
                {
                    switch (leadingTriviaList[i].Kind())
                    {
                    case SyntaxKind.WhitespaceTrivia:
                        break;

                    case SyntaxKind.EndOfLineTrivia:
                        isFirstOnLine = true;
                        done          = true;
                        break;

                    default:
                        done = true;
                        break;
                    }
                }
            }

            bool haveLeadingSpace;
            bool partOfUnaryExpression;
            bool startOfIndexer;

            var prevTokenIsOpenParen = prevToken.IsKind(SyntaxKind.OpenParenToken);

            switch (token.Parent.Kind())
            {
            case SyntaxKind.IfStatement:
            case SyntaxKind.DoStatement:
            case SyntaxKind.WhileStatement:
            case SyntaxKind.ForStatement:
            case SyntaxKind.ForEachStatement:
            case SyntaxKind.SwitchStatement:
            case SyntaxKind.FixedStatement:
            case SyntaxKind.LockStatement:
            case SyntaxKind.UsingStatement:
            case SyntaxKind.CatchDeclaration:
            case SyntaxKind.CatchFilterClause:
                haveLeadingSpace = true;
                break;

            case SyntaxKind.ArgumentList:
            case SyntaxKind.AttributeArgumentList:
            case SyntaxKind.CheckedExpression:
            case SyntaxKind.UncheckedExpression:
            case SyntaxKind.ConstructorConstraint:
            case SyntaxKind.DefaultExpression:
            case SyntaxKind.SizeOfExpression:
            case SyntaxKind.TypeOfExpression:
            default:
                haveLeadingSpace = false;
                break;

            case SyntaxKindEx.ParenthesizedVariableDesignation:
                haveLeadingSpace = true;
                break;

            case SyntaxKind.ParenthesizedExpression:
            case SyntaxKindEx.TupleExpression:
                if (prevToken.Parent.IsKind(SyntaxKind.Interpolation) ||
                    token.Parent.Parent.IsKind(SyntaxKindEx.RangeExpression))
                {
                    haveLeadingSpace = false;
                    break;
                }

                partOfUnaryExpression = prevToken.Parent is PrefixUnaryExpressionSyntax;
                startOfIndexer        = prevToken.IsKind(SyntaxKind.OpenBracketToken);
                var partOfCastExpression = prevToken.IsKind(SyntaxKind.CloseParenToken) && prevToken.Parent.IsKind(SyntaxKind.CastExpression);

                haveLeadingSpace = !partOfUnaryExpression && !startOfIndexer && !partOfCastExpression;
                break;

            case SyntaxKind.CastExpression:
                partOfUnaryExpression = prevToken.Parent is PrefixUnaryExpressionSyntax;
                startOfIndexer        = prevToken.IsKind(SyntaxKind.OpenBracketToken);
                var consecutiveCast     = prevToken.IsKind(SyntaxKind.CloseParenToken) && prevToken.Parent.IsKind(SyntaxKind.CastExpression);
                var partOfInterpolation = prevToken.IsKind(SyntaxKind.OpenBraceToken) && prevToken.Parent.IsKind(SyntaxKind.Interpolation);

                haveLeadingSpace = !partOfUnaryExpression && !startOfIndexer && !consecutiveCast && !partOfInterpolation;
                break;

            case SyntaxKind.ParameterList:
                var partOfLambdaExpression = token.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression);
                haveLeadingSpace = partOfLambdaExpression;
                break;

            case SyntaxKindEx.TupleType:
                // Comma covers tuple types in parameters and nested within other tuple types.
                // 'out', 'ref', 'in', 'params' parameters are covered by IsKeywordKind.
                // Return types are handled by a helper.
                haveLeadingSpace = prevToken.IsKind(SyntaxKind.CommaToken) ||
                                   SyntaxFacts.IsKeywordKind(prevToken.Kind()) ||
                                   ((TypeSyntax)token.Parent).GetContainingNotEnclosingType().IsReturnType();
                break;
            }

            // Ignore spacing before if another opening parenthesis is before this.
            // That way the first opening parenthesis will report any spacing errors.
            if (!prevTokenIsOpenParen && !precededByKeyword)
            {
                var hasLeadingComment = (leadingTriviaList.Count > 0) && leadingTriviaList.Last().IsKind(SyntaxKind.MultiLineCommentTrivia);
                var hasLeadingSpace   = (leadingTriviaList.Count > 0) && leadingTriviaList.Last().IsKind(SyntaxKind.WhitespaceTrivia);

                if (!isFirstOnLine && !hasLeadingComment && (haveLeadingSpace != hasLeadingSpace))
                {
                    if (haveLeadingSpace)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DescriptorPreceded, token.GetLocation(), TokenSpacingProperties.InsertPreceding));
                    }
                    else
                    {
                        context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), TokenSpacingProperties.RemovePreceding));
                    }
                }
            }

            if (token.IsFollowedByWhitespace())
            {
                context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowed, token.GetLocation(), TokenSpacingProperties.RemoveFollowingPreserveLayout));
            }
        }
예제 #16
0
        private static void HandleColonToken(SyntaxTreeAnalysisContext context, SyntaxToken token)
        {
            if (token.IsMissing)
            {
                return;
            }

            bool requireBefore;

            switch (token.Parent.Kind())
            {
            case SyntaxKind.BaseList:
            case SyntaxKind.BaseConstructorInitializer:
            case SyntaxKind.ThisConstructorInitializer:
            case SyntaxKind.TypeParameterConstraintClause:
            case SyntaxKind.ConditionalExpression:
                requireBefore = true;
                break;

            case SyntaxKind.LabeledStatement:
            case SyntaxKind.CaseSwitchLabel:
            case SyntaxKind.DefaultSwitchLabel:
            // NameColon is not explicitly listed in the description of this warning, but the behavior is inferred
            case SyntaxKind.NameColon:
                requireBefore = false;
                break;

            default:
                return;
            }

            // check for a following space
            bool missingFollowingSpace = true;

            if (token.HasTrailingTrivia)
            {
                if (token.TrailingTrivia.First().IsKind(SyntaxKind.WhitespaceTrivia))
                {
                    missingFollowingSpace = false;
                }
                else if (token.TrailingTrivia.First().IsKind(SyntaxKind.EndOfLineTrivia))
                {
                    missingFollowingSpace = false;
                }
            }

            bool hasPrecedingSpace = token.HasLeadingTrivia;

            if (!hasPrecedingSpace)
            {
                // only the first token on the line has leading trivia, and those are ignored
                SyntaxToken precedingToken = token.GetPreviousToken();
                var         combinedTrivia = TriviaHelper.MergeTriviaLists(precedingToken.TrailingTrivia, token.LeadingTrivia);
                if (combinedTrivia.Count > 0 && !combinedTrivia.Last().IsKind(SyntaxKind.MultiLineCommentTrivia))
                {
                    hasPrecedingSpace = true;
                }
            }

            if (missingFollowingSpace && requireBefore && !hasPrecedingSpace)
            {
                // colon must{} be {preceded}{ and followed} by a space
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "preceded", " and followed"));
            }
            else if (missingFollowingSpace)
            {
                // colon must{} be {followed}{} by a space
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "followed", string.Empty));
            }
            else if (hasPrecedingSpace != requireBefore)
            {
                // colon must{ not}? be {preceded}{} by a space
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), requireBefore ? string.Empty : " not", "preceded", string.Empty));
            }
        }