/// <summary> /// Gets the first token on the textline that the given token is on. /// </summary> /// <param name="token">The token used to determine the textline.</param> /// <returns>The first token on the textline of the given token.</returns> public static SyntaxToken GetFirstTokenOnTextLine(SyntaxToken token) { while (true) { var precedingToken = token.GetPreviousToken(); if (precedingToken.IsKind(SyntaxKind.None)) { return token; } if (precedingToken.GetLine() < token.GetLine()) { return token; } token = precedingToken; } }
private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace = token.IsFirstInLine() || token.IsPrecededByWhitespace(context.CancellationToken); bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); bool precedesStickyCharacter; bool allowEndOfLine = false; bool preserveLayout = false; bool suppressFollowingSpaceError = false; SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.OpenParenToken: // Allow a space between an open and a close paren when: // - they are part of an if statement // - they are on the same line // - the open paren is part of a parenthesized expression or a tuple expression. precedesStickyCharacter = !(token.Parent.IsKind(SyntaxKind.IfStatement) && (token.GetLine() == nextToken.GetLine()) && (nextToken.Parent.IsKind(SyntaxKind.ParenthesizedExpression) || nextToken.Parent.IsKind(SyntaxKindEx.TupleExpression))); break; case SyntaxKind.CloseParenToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.CloseBracketToken: case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: case SyntaxKind.DoubleQuoteToken: case SyntaxKindEx.DotDotToken: precedesStickyCharacter = true; break; case SyntaxKind.GreaterThanToken: precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.TypeArgumentList); break; case SyntaxKind.QuestionToken: if (nextToken.Parent.IsKind(SyntaxKind.ConditionalAccessExpression)) { // allow a space for this case, but only if the ')' character is the last on the line allowEndOfLine = true; precedesStickyCharacter = true; } else { // A space follows unless this is a nullable tuple type precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.NullableType); } break; case SyntaxKind.PlusToken: precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.UnaryPlusExpression); // this will be reported as SA1022 suppressFollowingSpaceError = precedesStickyCharacter; break; case SyntaxKind.MinusToken: precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.UnaryMinusExpression); // this will be reported as SA1021 suppressFollowingSpaceError = precedesStickyCharacter; break; case SyntaxKind.DotToken: // allow a space for this case, but only if the ')' character is the last on the line allowEndOfLine = true; precedesStickyCharacter = true; preserveLayout = nextToken.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression); break; case SyntaxKind.MinusGreaterThanToken: // allow a space for this case, but only if the ')' character is the last on the line allowEndOfLine = true; precedesStickyCharacter = true; break; case SyntaxKind.ColonToken: bool requireSpace = nextToken.Parent.IsKind(SyntaxKind.ConditionalExpression) || nextToken.Parent.IsKind(SyntaxKind.BaseConstructorInitializer) || nextToken.Parent.IsKind(SyntaxKind.ThisConstructorInitializer) || nextToken.Parent.IsKind(SyntaxKind.BaseList); precedesStickyCharacter = !requireSpace; break; case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: precedesStickyCharacter = true; suppressFollowingSpaceError = false; break; case SyntaxKind.CloseBraceToken: precedesStickyCharacter = nextToken.Parent is InterpolationSyntax; break; case SyntaxKind.ExclamationToken when nextToken.Parent.IsKind(SyntaxKindEx.SuppressNullableWarningExpression): precedesStickyCharacter = true; break; default: precedesStickyCharacter = false; break; } switch (token.Parent.Kind()) { case SyntaxKind.CastExpression: precedesStickyCharacter = true; break; default: break; } foreach (var trivia in token.TrailingTrivia) { if (trivia.IsKind(SyntaxKind.EndOfLineTrivia)) { break; } else if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) || trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)) { lastInLine = false; precedesStickyCharacter = false; break; } } if (precededBySpace) { // Closing parenthesis should{ not} be {preceded} by a space. ImmutableDictionary <string, string> properties; if (preserveLayout) { properties = TokenSpacingProperties.RemovePrecedingPreserveLayout; } else { properties = token.IsFirstInLine() ? TokenSpacingProperties.RemovePreceding : TokenSpacingProperties.RemoveImmediatePreceding; } context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), properties)); } if (!suppressFollowingSpaceError) { if (!precedesStickyCharacter && !followedBySpace && !lastInLine) { // Closing parenthesis should{} be {followed} by a space. var properties = TokenSpacingProperties.InsertFollowing; #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(), properties)); #pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor } else if (precedesStickyCharacter && followedBySpace && (!lastInLine || !allowEndOfLine)) { // Closing parenthesis should{ not} be {followed} by a space. var properties = TokenSpacingProperties.RemoveFollowing; #pragma warning disable RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor (https://github.com/dotnet/roslyn-analyzers/issues/4103) context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowed, token.GetLocation(), properties)); #pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor } } }
private static void Analyze( SyntaxNodeAnalysisContext context, SyntaxToken openParenOrBracketToken, SyntaxNode firstParameter, SyntaxNode secondParameter) { int firstParameterLine = firstParameter.GetLineSpan().StartLinePosition.Line; if (openParenOrBracketToken.GetLine() == firstParameterLine) { if (firstParameterLine != secondParameter.GetLineSpan().StartLinePosition.Line) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstParameter.GetLocation())); } } }
/// <summary> /// Gets a value indicating whether the <paramref name="token"/> is the first token in a line and it is only preceded by whitespace. /// </summary> /// <param name="token">The token to process.</param> /// <returns>true if the token is the first token in a line and it is only preceded by whitespace.</returns> internal static bool IsOnlyPrecededByWhitespaceInLine(this SyntaxToken token) { SyntaxToken precedingToken = token.GetPreviousToken(); if (!precedingToken.IsKind(SyntaxKind.None) && (precedingToken.GetLine() == token.GetLine())) { return(false); } var precedingTriviaList = TriviaHelper.MergeTriviaLists(precedingToken.TrailingTrivia, token.LeadingTrivia); for (var i = precedingTriviaList.Count - 1; i >= 0; i--) { switch (precedingTriviaList[i].Kind()) { case SyntaxKind.WhitespaceTrivia: break; case SyntaxKind.EndOfLineTrivia: return(true); default: return(false); } } return(true); }
private static void CheckBraces(SyntaxNodeAnalysisContext context, SyntaxToken openBraceToken, SyntaxToken closeBraceToken) { bool checkCloseBrace = true; int openBraceTokenLine = openBraceToken.GetLine(); if (openBraceTokenLine == closeBraceToken.GetLine()) { if (context.Node.IsKind(SyntaxKind.ArrayInitializerExpression)) { switch (context.Node.Parent.Kind()) { case SyntaxKind.EqualsValueClause: if (((EqualsValueClauseSyntax)context.Node.Parent).EqualsToken.GetLine() == openBraceTokenLine) { return; } break; case SyntaxKind.ArrayCreationExpression: if (((ArrayCreationExpressionSyntax)context.Node.Parent).NewKeyword.GetLine() == openBraceTokenLine) { return; } break; case SyntaxKind.ImplicitArrayCreationExpression: if (((ImplicitArrayCreationExpressionSyntax)context.Node.Parent).NewKeyword.GetLine() == openBraceTokenLine) { return; } break; case SyntaxKind.ArrayInitializerExpression: if (!InitializerExpressionSharesLine((InitializerExpressionSyntax)context.Node)) { return; } checkCloseBrace = false; break; default: break; } } else { switch (context.Node.Parent.Kind()) { case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: case SyntaxKind.UnknownAccessorDeclaration: if (((AccessorDeclarationSyntax)context.Node.Parent).Keyword.GetLine() == openBraceTokenLine) { // reported as SA1504, if at all return; } checkCloseBrace = false; break; default: // reported by SA1501 or SA1502 return; } } } CheckBraceToken(context, openBraceToken); if (checkCloseBrace) { CheckBraceToken(context, closeBraceToken); } }
/// <summary> /// Gets a value indicating whether the <paramref name="token"/> is last in line. /// </summary> /// <param name="token">The token to process.</param> /// <returns>true if token is last in line, otherwise false.</returns> internal static bool IsLastInLine(this SyntaxToken token) { SyntaxToken nextToken = token.GetNextToken(); return(nextToken.IsKind(SyntaxKind.None) || token.GetEndLine() < nextToken.GetLine()); }
/// <summary> /// Gets a value indicating whether the <paramref name="token"/> is first in line. /// </summary> /// <param name="token">The token to process.</param> /// <returns>true if token is first in line, otherwise false.</returns> internal static bool IsFirstInLine(this SyntaxToken token) { SyntaxToken previousToken = token.GetPreviousToken(); return(previousToken.IsKind(SyntaxKind.None) || previousToken.GetEndLine() < token.GetLine()); }
public override string ToString() { return($"строки {Open.GetLine() + 1}–{Close.GetLine() + 1}"); }
public static SyntaxToken GetFirstTokenAtLine(SyntaxToken token) { var tokenLineSpanStart = token.SyntaxTree.GetText().Lines[token.GetLine()].Start; return(token.SyntaxTree.GetRoot().FindToken(tokenLineSpanStart)); }