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())); } }
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())); } }
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); }
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); }
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)); } } }
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 } }
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)); } }
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)); } }