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); } }
private static SyntaxToken StripViolatingWhitespace(SyntaxToken token) { SyntaxToken result = token; var trailingWhitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(token.LeadingTrivia); if (trailingWhitespaceIndex != -1) { var newTriviaList = SyntaxFactory.TriviaList(token.LeadingTrivia.Take(trailingWhitespaceIndex)); result = token.WithLeadingTrivia(newTriviaList); } return(result); }
private static void HandleSyntaxTreeAction(SyntaxTreeAnalysisContext context) { var lastToken = context.Tree.GetRoot().GetLastToken(includeZeroWidth: true); if (lastToken.HasLeadingTrivia) { var leadingTrivia = lastToken.LeadingTrivia; var trailingWhitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(leadingTrivia); if (trailingWhitespaceIndex != -1) { var textSpan = TextSpan.FromBounds(leadingTrivia[trailingWhitespaceIndex].SpanStart, leadingTrivia[leadingTrivia.Count - 1].Span.End); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, textSpan))); } } }
private static SyntaxNode GetReplacement(ParenthesizedExpressionSyntax oldNode) { var leadingTrivia = SyntaxFactory.TriviaList(oldNode.OpenParenToken.GetAllTrivia().Concat(oldNode.Expression.GetLeadingTrivia())); var trailingTrivia = oldNode.Expression.GetTrailingTrivia().AddRange(oldNode.CloseParenToken.GetAllTrivia()); // Workaround for Roslyn not handling elastic markers for directive trivia correctly. if (!leadingTrivia.Any()) { var previousToken = oldNode.OpenParenToken.GetPreviousToken(); if (!previousToken.IsKind(SyntaxKind.OpenParenToken) && (TriviaHelper.IndexOfTrailingWhitespace(previousToken.TrailingTrivia) == -1)) { leadingTrivia = SyntaxFactory.TriviaList(SyntaxFactory.Space); } } return(oldNode.Expression .WithLeadingTrivia(leadingTrivia) .WithTrailingTrivia(trailingTrivia.Any() ? trailingTrivia : SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker))); }
private static async Task <Document> RemoveSemicolonTextAsync(Document document, SyntaxToken token, CancellationToken cancellationToken) { TextChange textChange; SourceText sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextLine line = sourceText.Lines.GetLineFromPosition(token.SpanStart); if (sourceText.ToString(line.Span).Trim() == token.Text) { // remove the line containing the semicolon token textChange = new TextChange(line.SpanIncludingLineBreak, string.Empty); return(document.WithText(sourceText.WithChanges(textChange))); } TextSpan spanToRemove; var whitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(token.LeadingTrivia); if (whitespaceIndex >= 0) { spanToRemove = TextSpan.FromBounds(token.LeadingTrivia[whitespaceIndex].Span.Start, token.Span.End); } else { var previousToken = token.GetPreviousToken(); whitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(previousToken.TrailingTrivia); if (whitespaceIndex >= 0) { spanToRemove = TextSpan.FromBounds(previousToken.TrailingTrivia[whitespaceIndex].Span.Start, token.Span.End); } else { spanToRemove = token.Span; } } textChange = new TextChange(spanToRemove, string.Empty); return(document.WithText(sourceText.WithChanges(textChange))); }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCopSettings settings) { var endOfFileToken = context.Tree.GetRoot().GetLastToken(includeZeroWidth: true); TextSpan reportedSpan = new TextSpan(endOfFileToken.SpanStart, 0); SyntaxTrivia precedingTrivia = default; bool checkPrecedingToken; if (endOfFileToken.HasLeadingTrivia) { var leadingTrivia = endOfFileToken.LeadingTrivia; var trailingWhitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(leadingTrivia); if (trailingWhitespaceIndex > 0) { checkPrecedingToken = false; reportedSpan = TextSpan.FromBounds(leadingTrivia[trailingWhitespaceIndex].SpanStart, reportedSpan.End); precedingTrivia = leadingTrivia[trailingWhitespaceIndex - 1]; } else if (trailingWhitespaceIndex == 0) { checkPrecedingToken = true; reportedSpan = TextSpan.FromBounds(leadingTrivia[trailingWhitespaceIndex].SpanStart, reportedSpan.End); } else { checkPrecedingToken = false; precedingTrivia = leadingTrivia.Last(); } } else { checkPrecedingToken = true; } if (checkPrecedingToken) { var previousToken = endOfFileToken.GetPreviousToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true); var trailingWhitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(previousToken.TrailingTrivia); if (trailingWhitespaceIndex > 0) { reportedSpan = TextSpan.FromBounds(previousToken.TrailingTrivia[trailingWhitespaceIndex].SpanStart, reportedSpan.End); precedingTrivia = previousToken.TrailingTrivia[trailingWhitespaceIndex - 1]; } else if (trailingWhitespaceIndex == 0) { reportedSpan = TextSpan.FromBounds(previousToken.TrailingTrivia[trailingWhitespaceIndex].SpanStart, reportedSpan.End); precedingTrivia = default; } else { if (previousToken.TrailingTrivia.Count > 0) { precedingTrivia = previousToken.TrailingTrivia.Last(); } } } if (precedingTrivia.IsDirective) { if (precedingTrivia.GetStructure() is DirectiveTriviaSyntax directiveTriviaSyntax && directiveTriviaSyntax.EndOfDirectiveToken.HasTrailingTrivia) { var trailingWhitespaceIndex = TriviaHelper.IndexOfTrailingWhitespace(directiveTriviaSyntax.EndOfDirectiveToken.TrailingTrivia); if (trailingWhitespaceIndex >= 0) { reportedSpan = TextSpan.FromBounds(directiveTriviaSyntax.EndOfDirectiveToken.TrailingTrivia[trailingWhitespaceIndex].SpanStart, reportedSpan.End); } } } else if (precedingTrivia.IsKind(SyntaxKind.EndOfLineTrivia)) { reportedSpan = TextSpan.FromBounds(precedingTrivia.SpanStart, reportedSpan.End); } SourceText sourceText = context.Tree.GetText(context.CancellationToken); string trailingWhitespaceText = sourceText.ToString(reportedSpan); int firstNewline = trailingWhitespaceText.IndexOf('\n'); int secondNewline = firstNewline >= 0 ? trailingWhitespaceText.IndexOf('\n', firstNewline + 1) : -1; DiagnosticDescriptor descriptorToReport; switch (settings.LayoutRules.NewlineAtEndOfFile) { case OptionSetting.Omit: if (firstNewline < 0) { return; } descriptorToReport = DescriptorOmit; break; case OptionSetting.Require: if (firstNewline >= 0 && firstNewline == trailingWhitespaceText.Length - 1) { return; } descriptorToReport = DescriptorRequire; break; case OptionSetting.Allow: default: if (secondNewline < 0) { // 1. A newline is allowed but not required // 2. If a newline is included, it cannot be followed by whitespace if (firstNewline < 0 || firstNewline == trailingWhitespaceText.Length - 1) { return; } } descriptorToReport = DescriptorAllow; break; } context.ReportDiagnostic(Diagnostic.Create(descriptorToReport, Location.Create(context.Tree, reportedSpan))); }