private static void HandleSyntaxTreeAxtion(SyntaxTreeAnalysisContext context) { var root = context.Tree.GetRoot(context.CancellationToken); var fileHeader = FileHeaderHelpers.ParseFileHeader(root); if (fileHeader.IsMissing || fileHeader.IsMalformed) { // this will be handled by SA1633 return; } var copyrightElement = fileHeader.GetElement("copyright"); if (copyrightElement == null) { // this will be handled by SA1634 return; } var companyAttribute = copyrightElement.Attribute("company"); if (string.IsNullOrWhiteSpace(companyAttribute?.Value)) { var location = fileHeader.GetElementLocation(context.Tree, copyrightElement); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
private static void HandleOpenBracketToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { bool firstInLine = token.IsFirstInLine(); bool precededBySpace = true; bool ignorePrecedingSpaceProblem = false; if (!firstInLine) { precededBySpace = token.IsPrecededByWhitespace(context.CancellationToken); // ignore if handled by SA1026 ignorePrecedingSpaceProblem = precededBySpace && token.GetPreviousToken().IsKind(SyntaxKind.NewKeyword); } bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); if (!firstInLine && precededBySpace && !ignorePrecedingSpaceProblem && !IsPartOfIndexInitializer(token)) { // Opening square bracket must {not be preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemovePreceding, "not be preceded")); } if (!lastInLine && followedBySpace) { // Opening square bracket must {not be followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemoveFollowing, "not be followed")); } }
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 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 void HandleOpenBracketToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { bool firstInLine = token.IsFirstInLine(); bool precededBySpace = true; bool ignorePrecedingSpaceProblem = false; if (!firstInLine) { precededBySpace = token.IsPrecededByWhitespace(); // ignore if handled by SA1026 ignorePrecedingSpaceProblem = precededBySpace && token.GetPreviousToken().IsKind(SyntaxKind.NewKeyword); } bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); if (!firstInLine && precededBySpace && !ignorePrecedingSpaceProblem && !lastInLine && followedBySpace) { // Opening square bracket must {neither preceded nor followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "neither preceded nor followed")); } else if (!firstInLine && precededBySpace && !ignorePrecedingSpaceProblem) { // Opening square bracket must {not be preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not be preceded")); } else if (!lastInLine && followedBySpace) { // Opening square bracket must {not be followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not be followed")); } }
private static void HandleOpenBracketToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } if (!token.Parent.IsKind(SyntaxKind.AttributeList)) { return; } if (token.IsLastInLine()) { return; } if (!token.HasTrailingTrivia) { return; } if (!token.TrailingTrivia[0].IsKind(SyntaxKind.WhitespaceTrivia)) { return; } // Opening attribute brackets must not be followed by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingCodeFixProvider.RemoveFollowing)); }
private static void HandleCloseBracketToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } if (!token.Parent.IsKind(SyntaxKind.AttributeList)) { return; } if (token.IsFirstInLine()) { return; } SyntaxToken precedingToken = token.GetPreviousToken(); if (!precedingToken.HasTrailingTrivia) { return; } if (!precedingToken.TrailingTrivia.Last().IsKind(SyntaxKind.WhitespaceTrivia)) { return; } // Closing attribute brackets must not be preceded by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemoveImmediatePreceding)); }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var descentNodes = syntaxRoot.DescendantNodes(descendIntoChildren: node => node != null && !node.IsKind(SyntaxKind.ClassDeclaration)); string foundClassName = null; bool isPartialClass = false; foreach (var node in descentNodes) { if (node.IsKind(SyntaxKind.ClassDeclaration)) { ClassDeclarationSyntax classDeclaration = node as ClassDeclarationSyntax; if (foundClassName != null) { if (isPartialClass && foundClassName == classDeclaration.Identifier.Text) { continue; } var location = NamedTypeHelpers.GetNameOrIdentifierLocation(node); if (location != null) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } else { foundClassName = classDeclaration.Identifier.Text; isPartialClass = classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword); } } } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.IsWhitespaceOnly(context.CancellationToken)) { // Handling of empty documents is now the responsibility of the analyzers return; } var firstToken = context.Tree.GetRoot().GetFirstToken(includeZeroWidth: true); if (firstToken.HasLeadingTrivia) { var leadingTrivia = firstToken.LeadingTrivia; var firstNonBlankLineTriviaIndex = TriviaHelper.IndexOfFirstNonBlankLineTrivia(leadingTrivia); switch (firstNonBlankLineTriviaIndex) { case 0: // no blank lines break; case -1: // only blank lines context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, leadingTrivia.Span))); break; default: var textSpan = TextSpan.FromBounds(leadingTrivia[0].Span.Start, leadingTrivia[firstNonBlankLineTriviaIndex].Span.Start); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, textSpan))); break; } } }
// If you want a full implementation of this analyzer with system tests and a code fix, go to // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1120CommentsMustContainText.cs private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken); foreach (var node in root.DescendantTrivia()) { switch (node.Kind()) { case SyntaxKind.SingleLineCommentTrivia: // Remove the leading // from the comment var commentText = node.ToString().Substring(2); int index = 0; var list = TriviaHelper.GetContainingTriviaList(node, out index); bool isFirst = IsFirstComment(list, index); bool isLast = IsLastComment(list, index); if (string.IsNullOrWhiteSpace(commentText) && (isFirst || isLast)) { var diagnostic = Diagnostic.Create(Rule, node.GetLocation()); context.ReportDiagnostic(diagnostic); } break; } } }
private static void HandleSyntaxTreeAxtion(SyntaxTreeAnalysisContext context) { var root = context.Tree.GetRoot(context.CancellationToken); var fileHeader = FileHeaderHelpers.ParseFileHeader(root); if (fileHeader.IsMissing || fileHeader.IsMalformed) { // this will be handled by SA1633 return; } var copyrightElement = fileHeader.GetElement("copyright"); if (copyrightElement == null) { // this will be handled by SA1634 return; } var fileAttribute = copyrightElement.Attribute("file"); if (fileAttribute == null) { // this will be handled by SA1637 return; } var fileName = Path.GetFileName(context.Tree.FilePath); if (!fileAttribute.Value.Equals(fileName, StringComparison.Ordinal)) { var location = fileHeader.GetElementLocation(context.Tree, copyrightElement); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } }
private static void CheckTrivias(IEnumerable<SyntaxTrivia> trivias, SyntaxTreeAnalysisContext context) { var shouldReport = true; foreach (var trivia in trivias) { // comment start is checked because of https://github.com/dotnet/roslyn/issues/10003 if (!trivia.ToFullString().TrimStart().StartsWith("/**", StringComparison.Ordinal) && trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)) { CheckMultilineComment(context, trivia); shouldReport = true; continue; } if (!trivia.ToFullString().TrimStart().StartsWith("///", StringComparison.Ordinal) && trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) && shouldReport) { var triviaContent = GetTriviaContent(trivia); if (!IsCode(triviaContent)) { continue; } context.ReportDiagnostic(Diagnostic.Create(Rule, trivia.GetLocation())); shouldReport = false; } } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.Options.DocumentationMode != DocumentationMode.Diagnose) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Tree.GetLocation(new TextSpan(0, 0)))); } }
public void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.Options.DocumentationMode == DocumentationMode.None) { Volatile.Write(ref this.documentationAnalysisDisabled, true); } }
private static void HandleLessThanToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } switch (token.Parent.Kind()) { case SyntaxKind.TypeArgumentList: case SyntaxKind.TypeParameterList: break; default: // not a generic bracket return; } bool firstInLine = token.IsFirstInLine(); bool precededBySpace = firstInLine || token.IsPrecededByWhitespace(); bool followedBySpace = token.IsFollowedByWhitespace(); if (!firstInLine && precededBySpace) { // Opening generic brackets must not be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "preceded")); } if (followedBySpace) { // Opening generic brackets must not be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "followed")); } }
private static void HandleOpenBraceToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool followedBySpace = token.IsFollowedByWhitespace(); if (token.Parent is InterpolationSyntax) { if (followedBySpace) { // Opening curly bracket must{} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), " not", "followed")); } return; } bool precededBySpace = token.IsFirstInLine() || token.IsPrecededByWhitespace(); if (!precededBySpace) { // Opening curly bracket must{} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "preceded")); } if (!token.IsLastInLine() && !followedBySpace) { // Opening curly bracket must{} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "followed")); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var firstToken = context.Tree.GetRoot().GetFirstToken(includeZeroWidth: true); if (firstToken.HasLeadingTrivia) { var leadingTrivia = firstToken.LeadingTrivia; var firstNonBlankLineTriviaIndex = TriviaHelper.IndexOfFirstNonBlankLineTrivia(leadingTrivia); switch (firstNonBlankLineTriviaIndex) { case 0: // no blank lines break; case -1: // only blank lines context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, leadingTrivia.Span))); break; default: var textSpan = TextSpan.FromBounds(leadingTrivia[0].Span.Start, leadingTrivia[firstNonBlankLineTriviaIndex].Span.Start); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, textSpan))); break; } } }
public static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCopSettings settings) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var firstTypeDeclaration = GetFirstTypeDeclaration(syntaxRoot); if (firstTypeDeclaration == null) { return; } if (firstTypeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) { return; } string suffix; var fileName = FileNameHelpers.GetFileNameAndSuffix(context.Tree.FilePath, out suffix); var expectedFileName = FileNameHelpers.GetConventionalFileName(firstTypeDeclaration, settings.DocumentationRules.FileNamingConvention); if (string.Compare(fileName, expectedFileName, StringComparison.OrdinalIgnoreCase) != 0) { if (settings.DocumentationRules.FileNamingConvention == FileNamingConvention.StyleCop && string.Compare(fileName, FileNameHelpers.GetSimpleFileName(firstTypeDeclaration), StringComparison.OrdinalIgnoreCase) == 0) { return; } var properties = ImmutableDictionary.Create<string, string>() .Add(ExpectedFileNameKey, expectedFileName + suffix); context.ReportDiagnostic(Diagnostic.Create(Descriptor, firstTypeDeclaration.Identifier.GetLocation(), properties)); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var descentNodes = syntaxRoot.DescendantNodes(descendIntoChildren: node => node != null && !node.IsKind(SyntaxKind.ClassDeclaration)); bool foundNode = false; foreach (var node in descentNodes) { if (node.IsKind(SyntaxKind.NamespaceDeclaration)) { if (foundNode) { var location = NamedTypeHelpers.GetNameOrIdentifierLocation(node); if (location != null) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } else { foundNode = true; } } } }
private static void AnalyzeTree(SyntaxTreeAnalysisContext context) { var tree = context.Tree; var emptyStrings = tree.GetRoot().DescendantTokens() .Where(x => x.RawKind == (int)SyntaxKind.StringLiteralToken && string.IsNullOrEmpty(x.ValueText)).ToList(); foreach (var s in emptyStrings) { // Skip if it is inside method parameter definition or as case switch or a attribute argument. if (s.Parent.Parent.Parent.IsKind(SyntaxKind.Parameter) || s.Parent.Parent.IsKind(SyntaxKind.CaseSwitchLabel) || s.Parent.Parent.IsKind(SyntaxKind.AttributeArgument)) { continue; } FieldDeclarationSyntax fieldSyntax = s.Parent.Parent.Parent.Parent.Parent as FieldDeclarationSyntax; if (fieldSyntax != null && fieldSyntax.DescendantTokens().Any(x => x.IsKind(SyntaxKind.ConstKeyword))) { continue; } var line = s.SyntaxTree.GetLineSpan(s.FullSpan); var diagnostic = Diagnostic.Create(Rule, s.GetLocation()); context.ReportDiagnostic(diagnostic); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken); foreach (var token in root.DescendantTokens().Where(t => t.IsKind(SyntaxKind.OpenParenToken))) { HandleOpenParenToken(context, token); } }
private void AnalyzeType(SyntaxTreeAnalysisContext context, TypeDeclarationSyntax typeDeclaration) { var numberOfFields = typeDeclaration.Members.Count(member => member is FieldDeclarationSyntax); if (numberOfFields > MaximumNumberOfFields) { context.ReportDiagnostic(Diagnostic.Create(Rule, typeDeclaration.Identifier.GetLocation(), typeDeclaration.Identifier.Text, numberOfFields)); } }
private void HandleDotToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } this.HandleMemberAccessSymbol(context, token); }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { byte[] preamble = context.Tree.Encoding.GetPreamble(); if (!IsUtf8Preamble(preamble)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, TextSpan.FromBounds(0, 0)))); } }
private async void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { var root = await context.Tree.GetRootAsync(); var typeDeclarations = root.DescendantNodesAndSelf().OfType<TypeDeclarationSyntax>(); foreach (var typeDeclaration in typeDeclarations) { AnalyzeType(context, typeDeclaration); } }
private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { var diagnostics = RenameTrackingTaggerProvider.GetDiagnosticsAsync(context.Tree, DiagnosticDescriptor, context.CancellationToken).WaitAndGetResult(context.CancellationToken); foreach (var diagnostic in diagnostics) { context.ReportDiagnostic(diagnostic); } }
private static void HandleIncrementDecrementToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } switch (token.Parent.Kind()) { case SyntaxKind.PreIncrementExpression: case SyntaxKind.PreDecrementExpression: if (token.HasTrailingTrivia) { string symbolName; if (token.IsKind(SyntaxKind.MinusMinusToken)) { symbolName = "Decrement"; } else { symbolName = "Increment"; } // {Increment|Decrement} symbol '{++|--}' must not be {followed} by a space. var properties = TokenSpacingProperties.RemoveFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, symbolName, token.Text, "followed")); } break; case SyntaxKind.PostIncrementExpression: case SyntaxKind.PostDecrementExpression: SyntaxToken previousToken = token.GetPreviousToken(); if (!previousToken.IsMissing && previousToken.HasTrailingTrivia) { string symbolName; if (token.IsKind(SyntaxKind.MinusMinusToken)) { symbolName = "Decrement"; } else { symbolName = "Increment"; } // {Increment|Decrement} symbol '{++|--}' must not be {preceded} by a space. var properties = TokenSpacingProperties.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, symbolName, token.Text, "preceded")); } break; default: return; } }
private void HandleWhitespaceTrivia(SyntaxTreeAnalysisContext context, SyntaxTrivia trivia) { if (trivia.ToFullString().IndexOf('\t') < 0) { return; } // Tabs must not be used. context.ReportDiagnostic(Diagnostic.Create(Descriptor, trivia.GetLocation())); }
private static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { // Find source files with documentation comment diagnostics turned off. if (context.Tree.Options.DocumentationMode != DocumentationMode.Diagnose) { // For all such files, produce a diagnostic. var diagnostic = Diagnostic.Create(Rule, Location.None, Path.GetFileName(context.Tree.FilePath)); context.ReportDiagnostic(diagnostic); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { byte[] preamble = context.Tree.Encoding.GetPreamble(); if (!IsUtf8Preamble(preamble)) { ImmutableDictionary<string, string> properties = ImmutableDictionary<string, string>.Empty.SetItem(EncodingProperty, context.Tree.Encoding?.WebName ?? "<null>"); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, TextSpan.FromBounds(0, 0)), properties)); } }
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: haveLeadingSpace = false; break; case SyntaxKind.ParenthesizedExpression: 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; default: haveLeadingSpace = false; 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)) { var properties = ImmutableDictionary.Create <string, string>() .Add(LocationKey, LocationPreceding) .Add(ActionKey, haveLeadingSpace ? ActionInsert : ActionRemove); context.ReportDiagnostic(Diagnostic.Create(haveLeadingSpace ? DescriptorPreceded : DescriptorNotPreceded, token.GetLocation(), properties.ToImmutableDictionary())); } } if (token.IsFollowedByWhitespace()) { var properties = ImmutableDictionary.Create <string, string>() .Add(LocationKey, LocationFollowing) .Add(ActionKey, ActionRemove); context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowed, token.GetLocation(), properties.ToImmutableDictionary())); } }
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 suppressFollowingSpaceError = false; SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.CloseBracketToken: case SyntaxKind.SemicolonToken: case SyntaxKind.CommaToken: case SyntaxKind.DoubleQuoteToken: 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 { precedesStickyCharacter = false; } 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: case SyntaxKind.MinusGreaterThanToken: // allow a space for these cases, 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); precedesStickyCharacter = !requireSpace; break; case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: precedesStickyCharacter = true; suppressFollowingSpaceError = false; break; case SyntaxKind.CloseBraceToken: precedesStickyCharacter = nextToken.Parent is InterpolationSyntax; 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. var properties = token.IsFirstInLine() ? TokenSpacingProperties.RemovePreceding : TokenSpacingProperties.RemoveImmediatePreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, " not", "preceded")); } if (!suppressFollowingSpaceError) { if (!precedesStickyCharacter && !followedBySpace && !lastInLine) { // Closing parenthesis should{} be {followed} by a space. var properties = TokenSpacingProperties.InsertFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, string.Empty, "followed")); } else if (precedesStickyCharacter && followedBySpace && (!lastInLine || !allowEndOfLine)) { // Closing parenthesis should{ not} be {followed} by a space. var properties = TokenSpacingProperties.RemoveFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, " not", "followed")); } } }
protected abstract void Recurse( SyntaxTreeAnalysisContext context, Dictionary <int, int> preferredOrder, ReportDiagnostic severity, SyntaxNode root);
/// <summary> /// Scans an entire document for lines with trailing whitespace. /// </summary> /// <param name="context">The context that provides the document to scan.</param> private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var root = context.Tree.GetRoot(context.CancellationToken); var text = context.Tree.GetText(context.CancellationToken); SyntaxTrivia previousTrivia = default(SyntaxTrivia); foreach (var trivia in root.DescendantTrivia(descendIntoTrivia: true)) { switch (trivia.Kind()) { case SyntaxKind.WhitespaceTrivia: break; case SyntaxKind.EndOfLineTrivia: if (previousTrivia.Span.End < trivia.SpanStart) { // Some token appeared between the previous trivia and the end of the line. break; } if (previousTrivia.IsKind(SyntaxKind.WhitespaceTrivia)) { // Report warning for whitespace token followed by the end of a line context.ReportDiagnostic(Diagnostic.Create(Descriptor, previousTrivia.GetLocation())); } else if (previousTrivia.IsKind(SyntaxKind.PreprocessingMessageTrivia)) { TextSpan trailinMessageWhitespace = FindTrailingWhitespace(text, previousTrivia.Span); if (!trailinMessageWhitespace.IsEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, trailinMessageWhitespace))); } } break; case SyntaxKind.SingleLineCommentTrivia: TextSpan trailingWhitespace = FindTrailingWhitespace(text, trivia.Span); if (!trailingWhitespace.IsEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, trailingWhitespace))); } break; case SyntaxKind.MultiLineCommentTrivia: var line = text.Lines.GetLineFromPosition(trivia.Span.Start); while (line.End <= trivia.Span.End) { trailingWhitespace = FindTrailingWhitespace(text, line.Span); if (!trailingWhitespace.IsEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, trailingWhitespace))); } if (line.EndIncludingLineBreak == text.Length) { // We've reached the end of the document. break; } line = text.Lines.GetLineFromPosition(line.EndIncludingLineBreak + 1); } break; case SyntaxKind.SingleLineDocumentationCommentTrivia: case SyntaxKind.MultiLineDocumentationCommentTrivia: SyntaxToken previousToken = default(SyntaxToken); foreach (var token in trivia.GetStructure().DescendantTokens(descendIntoTrivia: true)) { if (token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken) && previousToken.IsKind(SyntaxKind.XmlTextLiteralToken) && previousToken.Span.End == token.SpanStart) { trailingWhitespace = FindTrailingWhitespace(text, previousToken.Span); if (!trailingWhitespace.IsEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, trailingWhitespace))); } } previousToken = token; } break; default: break; } previousTrivia = trivia; } if (previousTrivia.IsKind(SyntaxKind.WhitespaceTrivia) && previousTrivia.Span.End == previousTrivia.SyntaxTree.Length) { // Report whitespace at the end of the last line in the document context.ReportDiagnostic(Diagnostic.Create(Descriptor, previousTrivia.GetLocation())); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken); foreach (var token in root.DescendantTokens()) { switch (token.Kind()) { case SyntaxKind.AwaitKeyword: case SyntaxKind.CaseKeyword: case SyntaxKind.CatchKeyword: 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.OrderByKeyword: case SyntaxKind.OutKeyword: case SyntaxKind.RefKeyword: case SyntaxKind.SelectKeyword: case SyntaxKind.SwitchKeyword: case SyntaxKind.UsingKeyword: case SyntaxKind.WhereKeyword: case SyntaxKind.WhileKeyword: case SyntaxKind.YieldKeyword: HandleRequiredSpaceToken(ref context, token); break; case SyntaxKind.CheckedKeyword: case SyntaxKind.UncheckedKeyword: if (token.GetNextToken().IsKind(SyntaxKind.OpenBraceToken)) { HandleRequiredSpaceToken(ref context, token); } else { HandleDisallowedSpaceToken(ref context, token); } break; case SyntaxKind.DefaultKeyword: if (token.Parent.IsKind(SyntaxKindEx.DefaultLiteralExpression)) { // Ignore spacing around a default literal expression for now break; } HandleDisallowedSpaceToken(ref context, token); break; case SyntaxKind.NameOfKeyword: case SyntaxKind.SizeOfKeyword: case SyntaxKind.TypeOfKeyword: HandleDisallowedSpaceToken(ref context, token); break; case SyntaxKind.NewKeyword: case SyntaxKind.StackAllocKeyword: HandleNewOrStackAllocKeywordToken(ref context, token); break; case SyntaxKind.ReturnKeyword: HandleReturnKeywordToken(ref context, token); break; case SyntaxKind.ThrowKeyword: HandleThrowKeywordToken(ref context, token); break; default: break; } } }
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 SyntaxKindEx.PositionalPatternClause: haveLeadingSpace = prevToken.IsKind(SyntaxKind.IsKeyword) || prevToken.IsKind(SyntaxKindEx.OrKeyword) || prevToken.IsKind(SyntaxKindEx.AndKeyword) || prevToken.IsKind(SyntaxKindEx.NotKeyword) || prevToken.IsKind(SyntaxKind.CommaToken); break; case SyntaxKindEx.ParenthesizedPattern: var partOfCastExpression = prevToken.IsKind(SyntaxKind.CloseParenToken) && prevToken.Parent.IsKind(SyntaxKind.CastExpression); haveLeadingSpace = !partOfCastExpression; 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); 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); var partOfRange = prevToken.IsKind(SyntaxKindEx.DotDotToken); haveLeadingSpace = !partOfUnaryExpression && !startOfIndexer && !consecutiveCast && !partOfInterpolation && !partOfRange; 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. // Attributes of parameters are covered by checking the previous token's parent. // Return types are handled by a helper. haveLeadingSpace = prevToken.IsKind(SyntaxKind.CommaToken) || SyntaxFacts.IsKeywordKind(prevToken.Kind()) || prevToken.Parent.IsKind(SyntaxKind.AttributeList) || ((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 void Analyze(SyntaxTreeAnalysisContext context) => context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, TextSpan.FromBounds(1000, 2000))));
public void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { context.ReportDiagnostic(Diagnostic.Create(s_syntaxDiagnosticDescriptor, context.Tree.GetRoot().GetFirstToken().GetLocation())); }
public BracesVisitor(SyntaxTreeAnalysisContext context) : base(SyntaxWalkerDepth.Token) { this.context = context; }
private void ProcessMemberDeclaration( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, MemberDeclarationSyntax member) { if (member.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax namespaceDeclaration)) { ProcessMembers(context, option, namespaceDeclaration.Members); } // If we have a class or struct, recurse inwards. if (member.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax typeDeclaration) || member.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration)) { ProcessMembers(context, option, typeDeclaration.Members); } #if false // Add this once we have the language version for C# that supports accessibility // modifiers on interface methods. if (option.Value == AccessibilityModifiersRequired.Always && member.IsKind(SyntaxKind.InterfaceDeclaration, out typeDeclaration)) { // Only recurse into an interface if the user wants accessibility modifiers on ProcessTypeDeclaration(context, generator, option, typeDeclaration); } #endif // Have to have a name to report the issue on. var name = member.GetNameToken(); if (name.Kind() == SyntaxKind.None) { return; } // Certain members never have accessibility. Don't bother reporting on them. if (!SyntaxFacts.CanHaveAccessibility(member)) { return; } // This analyzer bases all of its decisions on the accessibility var accessibility = SyntaxFacts.GetAccessibility(member); // Omit will flag any accessibility values that exist and are default // The other options will remove or ignore accessibility var isOmit = option.Value == AccessibilityModifiersRequired.OmitIfDefault; if (isOmit) { if (accessibility == Accessibility.NotApplicable) { return; } var parentKind = member.Parent.Kind(); switch (parentKind) { // Check for default modifiers in namespace and outside of namespace case SyntaxKind.CompilationUnit: case SyntaxKind.NamespaceDeclaration: { // Default is internal if (accessibility != Accessibility.Internal) { return; } } break; case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: { // Inside a type, default is private if (accessibility != Accessibility.Private) { return; } } break; default: return; // Unknown parent kind, don't do anything } } else { // Mode is always, so we have to flag missing modifiers if (accessibility != Accessibility.NotApplicable) { return; } } // Have an issue to flag, either add or remove. Report issue to user. var additionalLocations = ImmutableArray.Create(member.GetLocation()); context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, name.GetLocation(), option.Notification.Severity, additionalLocations: additionalLocations, properties: null)); }
protected override void ProcessCompilationUnit( SyntaxTreeAnalysisContext context, CodeStyleOption2 <AccessibilityModifiersRequired> option, CompilationUnitSyntax compilationUnit) { ProcessMembers(context, option, compilationUnit.Members); }
public static void ReportDiagnosticWhenActive(this SyntaxTreeAnalysisContext context, Diagnostic diagnostic) => ReportDiagnostic(new ReportingContext(context, diagnostic));
// Actions/SyntaxTree private static void OnSyntaxTree(SyntaxTreeAnalysisContext context) { }
private static void HandleCloseBracketToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } // attribute brackets are handled separately if (token.Parent.IsKind(SyntaxKind.AttributeList)) { return; } bool firstInLine = token.IsFirstInLine(); bool precededBySpace = firstInLine || token.IsPrecededByWhitespace(); bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); bool precedesSpecialCharacter; // Tests for this rule have a lot of exclusions which are supposed to be caught by other rules bool suppressFollowingSpaceError = true; if (!lastInLine) { SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.CloseBracketToken: case SyntaxKind.OpenParenToken: case SyntaxKind.CommaToken: case SyntaxKind.SemicolonToken: // TODO: "certain types of operator symbols" case SyntaxKind.DotToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.CloseParenToken: precedesSpecialCharacter = true; break; case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: precedesSpecialCharacter = true; suppressFollowingSpaceError = false; break; case SyntaxKind.GreaterThanToken: precedesSpecialCharacter = nextToken.Parent.IsKind(SyntaxKind.TypeArgumentList); break; case SyntaxKind.QuestionToken: precedesSpecialCharacter = nextToken.Parent.IsKind(SyntaxKind.ConditionalAccessExpression); break; case SyntaxKind.CloseBraceToken: precedesSpecialCharacter = nextToken.Parent is InterpolationSyntax; break; default: precedesSpecialCharacter = false; break; } } else { precedesSpecialCharacter = false; } if (!firstInLine && precededBySpace) { // Closing square bracket must{ not} be {preceded} by a space. var properties = new Dictionary <string, string> { [OpenCloseSpacingCodeFixProvider.LocationKey] = OpenCloseSpacingCodeFixProvider.LocationPreceding, [OpenCloseSpacingCodeFixProvider.ActionKey] = OpenCloseSpacingCodeFixProvider.ActionRemove }; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties.ToImmutableDictionary(), " not", "preceded")); } if (!lastInLine) { if (!precedesSpecialCharacter && !followedBySpace) { // Closing square bracket must{} be {followed} by a space. var properties = new Dictionary <string, string> { [OpenCloseSpacingCodeFixProvider.LocationKey] = OpenCloseSpacingCodeFixProvider.LocationFollowing, [OpenCloseSpacingCodeFixProvider.ActionKey] = OpenCloseSpacingCodeFixProvider.ActionInsert }; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties.ToImmutableDictionary(), string.Empty, "followed")); } else if (precedesSpecialCharacter && followedBySpace && !suppressFollowingSpaceError) { // Closing square brackets must {not} be {followed} by a space var properties = new Dictionary <string, string> { [OpenCloseSpacingCodeFixProvider.LocationKey] = OpenCloseSpacingCodeFixProvider.LocationFollowing, [OpenCloseSpacingCodeFixProvider.ActionKey] = OpenCloseSpacingCodeFixProvider.ActionRemove }; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties.ToImmutableDictionary(), " not", "followed")); } } }
/// <summary> /// Checks whether the given document is auto generated by a tool. /// </summary> /// <remarks> /// <para>This method uses <see cref="IsGeneratedDocument(SyntaxTree, ConcurrentDictionary{SyntaxTree, bool}, CancellationToken)"/> to determine which /// code is considered "generated".</para> /// </remarks> /// <param name="context">The analysis context for a <see cref="SyntaxTree"/>.</param> /// <param name="cache">The concurrent results cache.</param> /// <returns> /// <para><see langword="true"/> if the <see cref="SyntaxTree"/> contained in <paramref name="context"/> is /// located in generated code; otherwise, <see langword="false"/>.</para> /// </returns> internal static bool IsGeneratedDocument(this SyntaxTreeAnalysisContext context, ConcurrentDictionary <SyntaxTree, bool> cache) { return(IsGeneratedDocument(context.Tree, cache, context.CancellationToken)); }
private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { OnAbstractMember("SyntaxTree"); OnOptions(context.Options); }
private static void AnalyzeTree(SyntaxTreeAnalysisContext context, Compilation compilation) { if (context.IsGenerated()) { return; } if (!compilation.SyntaxTrees.Contains(context.Tree)) { return; } var semanticModel = compilation.GetSemanticModel(context.Tree); SyntaxNode root; if (!context.Tree.TryGetRoot(out root)) { return; } var types = GetTypesInRoot(root); foreach (var type in types) { var fieldDeclarations = type.ChildNodes().OfType <FieldDeclarationSyntax>(); var variablesToMakeReadonly = GetCandidateVariables(semanticModel, fieldDeclarations); var typeSymbol = semanticModel.GetDeclaredSymbol(type); if (typeSymbol == null) { continue; } var methods = typeSymbol.GetAllMethodsIncludingFromInnerTypes(); foreach (var method in methods) { foreach (var syntaxReference in method.DeclaringSyntaxReferences) { var syntaxRefSemanticModel = syntaxReference.SyntaxTree.Equals(context.Tree) ? semanticModel : compilation.GetSemanticModel(syntaxReference.SyntaxTree); var descendants = syntaxReference.GetSyntax().DescendantNodes().ToList(); var assignments = descendants.OfKind(SyntaxKind.SimpleAssignmentExpression, SyntaxKind.AddAssignmentExpression, SyntaxKind.AndAssignmentExpression, SyntaxKind.DivideAssignmentExpression, SyntaxKind.ExclusiveOrAssignmentExpression, SyntaxKind.LeftShiftAssignmentExpression, SyntaxKind.ModuloAssignmentExpression, SyntaxKind.MultiplyAssignmentExpression, SyntaxKind.OrAssignmentExpression, SyntaxKind.RightShiftAssignmentExpression, SyntaxKind.SubtractAssignmentExpression); foreach (AssignmentExpressionSyntax assignment in assignments) { var fieldSymbol = syntaxRefSemanticModel.GetSymbolInfo(assignment.Left).Symbol as IFieldSymbol; VerifyVariable(variablesToMakeReadonly, method, syntaxRefSemanticModel, assignment, fieldSymbol); } var postFixUnaries = descendants.OfKind(SyntaxKind.PostIncrementExpression, SyntaxKind.PostDecrementExpression); foreach (PostfixUnaryExpressionSyntax postFixUnary in postFixUnaries) { var fieldSymbol = syntaxRefSemanticModel.GetSymbolInfo(postFixUnary.Operand).Symbol as IFieldSymbol; VerifyVariable(variablesToMakeReadonly, method, syntaxRefSemanticModel, postFixUnary, fieldSymbol); } var preFixUnaries = descendants.OfKind(SyntaxKind.PreDecrementExpression, SyntaxKind.PreIncrementExpression); foreach (PrefixUnaryExpressionSyntax preFixUnary in preFixUnaries) { var fieldSymbol = syntaxRefSemanticModel.GetSymbolInfo(preFixUnary.Operand).Symbol as IFieldSymbol; VerifyVariable(variablesToMakeReadonly, method, syntaxRefSemanticModel, preFixUnary, fieldSymbol); } } } foreach (var readonlyVariable in variablesToMakeReadonly.Values) { var props = new Dictionary <string, string> { { "identifier", readonlyVariable.Identifier.Text } }.ToImmutableDictionary(); var diagnostic = Diagnostic.Create(Rule, readonlyVariable.GetLocation(), props, readonlyVariable.Identifier.Text); context.ReportDiagnostic(diagnostic); } } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var previousCommentNotOnOwnLine = false; foreach (var trivia in syntaxRoot.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))) { if (trivia.FullSpan.Start == 0) { // skip the trivia if it is at the start of the file previousCommentNotOnOwnLine = false; continue; } if (trivia.ToString().StartsWith("////", StringComparison.Ordinal)) { // ignore commented out code previousCommentNotOnOwnLine = false; continue; } int triviaIndex; var triviaList = TriviaHelper.GetContainingTriviaList(trivia, out triviaIndex); if (!IsOnOwnLine(triviaList, triviaIndex)) { // ignore comments after other code elements. previousCommentNotOnOwnLine = true; continue; } if (IsPrecededByBlankLine(triviaList, triviaIndex)) { // allow properly formatted blank line comments. previousCommentNotOnOwnLine = false; continue; } if (!previousCommentNotOnOwnLine && IsPrecededBySingleLineCommentOrDocumentation(triviaList, triviaIndex)) { // allow consecutive single line comments. previousCommentNotOnOwnLine = false; continue; } previousCommentNotOnOwnLine = false; if (IsAtStartOfScope(trivia)) { // allow single line comment at scope start. continue; } if (IsPrecededByDirectiveTrivia(triviaList, triviaIndex)) { // allow single line comment that is preceded by some directive trivia (if, elif, else) continue; } var diagnosticSpan = TextSpan.FromBounds(trivia.SpanStart, trivia.SpanStart + 2); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, diagnosticSpan))); } }
private static void HandleSyntaxTreeAnalysis(SyntaxTreeAnalysisContext context, Compilation compilation) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); foreach (var trivia in syntaxRoot.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))) { if (trivia.ToString().StartsWith("////", StringComparison.Ordinal)) { // ignore commented out code continue; } int triviaIndex; // PERF: Explicitly cast to IReadOnlyList so we only box once. var triviaList = TriviaHelper.GetContainingTriviaList(trivia, out triviaIndex); if (!IsOnOwnLine(triviaList, triviaIndex)) { // ignore comments after other code elements. continue; } if (IsPartOfFileHeader(triviaList, triviaIndex)) { // ignore comments that are part of the file header. continue; } var trailingBlankLineCount = GetTrailingBlankLineCount(triviaList, ref triviaIndex); if (trailingBlankLineCount == 0) { // ignore comments that are not followed by a blank line continue; } else if (trailingBlankLineCount > 1) { if (!compilation.IsAnalyzerSuppressed(SA1507CodeMustNotContainMultipleBlankLinesInARow.Descriptor)) { // ignore comments that are followed by multiple blank lines -> the multiple blank lines will be reported by SA1507 continue; } } else { if (triviaIndex < triviaList.Count) { switch (triviaList[triviaIndex].Kind()) { case SyntaxKind.SingleLineCommentTrivia: case SyntaxKind.SingleLineDocumentationCommentTrivia: case SyntaxKind.MultiLineCommentTrivia: case SyntaxKind.MultiLineDocumentationCommentTrivia: // ignore a single blank line in between two comments. continue; } } } var diagnosticSpan = TextSpan.FromBounds(trivia.SpanStart, trivia.SpanStart + 2); context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, diagnosticSpan))); } }
private static void HandleGreaterThanToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } switch (token.Parent.Kind()) { case SyntaxKind.TypeArgumentList: case SyntaxKind.TypeParameterList: break; default: // not a generic bracket return; } bool firstInLine = token.IsFirstInLine(); bool lastInLine = token.IsLastInLine(); bool precededBySpace = firstInLine || token.IsPrecededByWhitespace(context.CancellationToken); bool followedBySpace = token.IsFollowedByWhitespace(); bool allowTrailingNoSpace; bool allowTrailingSpace; if (!lastInLine) { SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.OpenParenToken: // DotToken isn't listed above, but it's required for reasonable member access formatting case SyntaxKind.DotToken: // CommaToken isn't listed above, but it's required for reasonable nested generic type arguments formatting case SyntaxKind.CommaToken: // OpenBracketToken isn't listed above, but it's required for reasonable array type formatting case SyntaxKind.OpenBracketToken: // SemicolonToken isn't listed above, but it's required for reasonable using alias declaration formatting case SyntaxKind.SemicolonToken: case SyntaxKind.ColonToken when nextToken.Parent.IsKind(SyntaxKindEx.CasePatternSwitchLabel): allowTrailingNoSpace = true; allowTrailingSpace = false; break; case SyntaxKind.CloseParenToken: case SyntaxKind.GreaterThanToken: allowTrailingNoSpace = true; allowTrailingSpace = true; break; case SyntaxKind.QuestionToken: allowTrailingNoSpace = nextToken.Parent.IsKind(SyntaxKind.NullableType); allowTrailingSpace = true; break; default: allowTrailingNoSpace = false; allowTrailingSpace = true; break; } } else { allowTrailingNoSpace = true; allowTrailingSpace = true; } if (!firstInLine && precededBySpace) { // Closing generic bracket should{ not} be {preceded} by a space. var properties = TokenSpacingProperties.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), properties)); } if (!lastInLine) { if (!allowTrailingNoSpace && !followedBySpace) { // Closing generic bracket 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 (!allowTrailingSpace && followedBySpace) { // Closing generic bracket 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 HandleDisallowedSpaceToken(ref SyntaxTreeAnalysisContext context, SyntaxToken token) => HandleDisallowedSpaceToken(ReportSyntaxTreeDiagnostic, ref context, token);
/// <summary> /// Gets the StyleCop settings. /// </summary> /// <param name="context">The context that will be used to determine the StyleCop settings.</param> /// <param name="cancellationToken">The cancellation token that the operation will observe.</param> /// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns> internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisContext context, CancellationToken cancellationToken) { return(context.Options.GetStyleCopSettings(cancellationToken)); }
public GatherVisitor(SyntaxTreeAnalysisContext ctx, SemanticModel semanticModel) { this.ctx = ctx; this.semanticModel = semanticModel; }
public static SyntaxTree GetSyntaxTree(this SyntaxTreeAnalysisContext context) => context.Tree;
private static void ReportIncorrectTabUsage(SyntaxTreeAnalysisContext context, IndentationSettings indentationSettings, ImmutableArray <TextSpan> excludedSpans) { SyntaxTree syntaxTree = context.Tree; SourceText sourceText = syntaxTree.GetText(context.CancellationToken); string completeText = sourceText.ToString(); int length = completeText.Length; bool useTabs = indentationSettings.UseTabs; int tabSize = indentationSettings.TabSize; int excludedSpanIndex = 0; var lastExcludedSpan = new TextSpan(completeText.Length, 0); TextSpan nextExcludedSpan = !excludedSpans.IsEmpty ? excludedSpans[0] : lastExcludedSpan; int lastLineStart = 0; for (int startIndex = 0; startIndex < length; startIndex++) { if (startIndex == nextExcludedSpan.Start) { startIndex = nextExcludedSpan.End - 1; excludedSpanIndex++; nextExcludedSpan = excludedSpanIndex < excludedSpans.Length ? excludedSpans[excludedSpanIndex] : lastExcludedSpan; continue; } int tabCount = 0; bool containsSpaces = false; bool tabAfterSpace = false; switch (completeText[startIndex]) { case ' ': containsSpaces = true; break; case '\t': tabCount++; break; case '\r': case '\n': // Handle newlines. We can ignore CR/LF/CRLF issues because we are only tracking column position // in a line, and not the line numbers themselves. lastLineStart = startIndex + 1; continue; default: continue; } int endIndex; for (endIndex = startIndex + 1; endIndex < length; endIndex++) { if (endIndex == nextExcludedSpan.Start) { break; } if (completeText[endIndex] == ' ') { containsSpaces = true; } else if (completeText[endIndex] == '\t') { tabCount++; tabAfterSpace = containsSpaces; } else { break; } } if (useTabs && startIndex == lastLineStart) { // For the case we care about in the following condition (tabAfterSpace is false), spaceCount is // the number of consecutive trailing spaces. int spaceCount = (endIndex - startIndex) - tabCount; if (tabAfterSpace || spaceCount >= tabSize) { context.ReportDiagnostic( Diagnostic.Create( Descriptor, Location.Create(syntaxTree, TextSpan.FromBounds(startIndex, endIndex)), ConvertToTabsProperties)); } } else { if (tabCount > 0) { context.ReportDiagnostic( Diagnostic.Create( Descriptor, Location.Create(syntaxTree, TextSpan.FromBounds(startIndex, endIndex)), ConvertToSpacesProperties)); } } // Make sure to not analyze overlapping spans startIndex = endIndex - 1; } }
protected abstract void ProcessCompilationUnit(SyntaxTreeAnalysisContext context, SyntaxGenerator generator, CodeStyleOption <AccessibilityModifiersRequired> option, TCompilationUnitSyntax compilationUnitSyntax);
private static void AnalyzeWhitespace(SyntaxTreeAnalysisContext context) { var walker = new WhitespaceWalker(context); walker.Visit(context.Tree.GetRoot()); }
public WhitespaceWalker(SyntaxTreeAnalysisContext context) : base(SyntaxWalkerDepth.StructuredTrivia) { this.context = context; }
private static void AnalyzeTree(SyntaxTreeAnalysisContext context, Compilation compilation) { if (context.IsGenerated()) { return; } if (!compilation.SyntaxTrees.Contains(context.Tree)) { return; } var semanticModel = compilation.GetSemanticModel(context.Tree); SyntaxNode root; if (!context.Tree.TryGetRoot(out root)) { return; } var types = GetTypesInRoot(root); foreach (var type in types) { var fieldDeclarations = type.ChildNodes().OfType <FieldDeclarationSyntax>(); var variablesToMakeReadonly = GetCandidateVariables(semanticModel, fieldDeclarations); var typeSymbol = semanticModel.GetDeclaredSymbol(type); if (typeSymbol == null) { continue; } var methods = typeSymbol.GetAllMethodsIncludingFromInnerTypes(); foreach (var method in methods) { foreach (var syntaxReference in method.DeclaringSyntaxReferences) { var syntaxRefSemanticModel = syntaxReference.SyntaxTree.Equals(context.Tree) ? semanticModel : compilation.GetSemanticModel(syntaxReference.SyntaxTree); var assignments = syntaxReference.GetSyntax().DescendantNodes().OfType <AssignmentExpressionSyntax>(); foreach (var assignment in assignments) { var fieldSymbol = syntaxRefSemanticModel.GetSymbolInfo(assignment.Left).Symbol as IFieldSymbol; if (fieldSymbol == null) { continue; } if (method.MethodKind == MethodKind.StaticConstructor && fieldSymbol.IsStatic) { AddVariableThatWasSkippedBeforeBecauseItLackedAInitializer(variablesToMakeReadonly, fieldSymbol); } else if (method.MethodKind == MethodKind.Constructor && !fieldSymbol.IsStatic) { AddVariableThatWasSkippedBeforeBecauseItLackedAInitializer(variablesToMakeReadonly, fieldSymbol); } else { RemoveVariableThatHasAssignment(variablesToMakeReadonly, fieldSymbol); } } } } foreach (var readonlyVariable in variablesToMakeReadonly.Values) { var props = new Dictionary <string, string> { { "identifier", readonlyVariable.Identifier.Text } }.ToImmutableDictionary(); var diagnostic = Diagnostic.Create(Rule, readonlyVariable.GetLocation(), props, readonlyVariable.Identifier.Text); context.ReportDiagnostic(diagnostic); } } }
public static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCopSettings settings, Compilation compilation) { var root = context.Tree.GetRoot(context.CancellationToken); // don't process empty files if (root.FullSpan.IsEmpty) { return; } if (settings.DocumentationRules.XmlHeader) { var fileHeader = FileHeaderHelpers.ParseXmlFileHeader(root); if (fileHeader.IsMissing) { context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMissing, fileHeader.GetLocation(context.Tree))); return; } if (fileHeader.IsMalformed) { context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMalformed, fileHeader.GetLocation(context.Tree))); return; } if (!compilation.IsAnalyzerSuppressed(SA1634Identifier)) { CheckCopyrightHeader(context, settings.DocumentationRules, compilation, fileHeader); } if (!compilation.IsAnalyzerSuppressed(SA1639Identifier)) { CheckSummaryHeader(context, compilation, fileHeader); } } else { var fileHeader = FileHeaderHelpers.ParseFileHeader(root); if (fileHeader.IsMissing) { context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMissing, fileHeader.GetLocation(context.Tree))); return; } if (!compilation.IsAnalyzerSuppressed(SA1635Identifier)) { if (string.IsNullOrWhiteSpace(fileHeader.CopyrightText)) { context.ReportDiagnostic(Diagnostic.Create(SA1635Descriptor, fileHeader.GetLocation(context.Tree))); return; } if (compilation.IsAnalyzerSuppressed(SA1636Identifier)) { return; } if (!CompareCopyrightText(context, settings.DocumentationRules, fileHeader.CopyrightText)) { context.ReportDiagnostic(Diagnostic.Create(SA1636Descriptor, fileHeader.GetLocation(context.Tree))); return; } } } }