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; } } }
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; } } }
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 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 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); } } } }
// 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 HandleSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.Options.DocumentationMode != DocumentationMode.Diagnose) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Tree.GetLocation(new TextSpan(0, 0)))); } }
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 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 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 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 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 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 HandleCloseBraceToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace = token.IsFirstInLine() || token.IsPrecededByWhitespace(); if (token.Parent is InterpolationSyntax) { if (precededBySpace) { // Closing curly bracket must{ not} be {preceded} by a space. var properties = TokenSpacingCodeFixProvider.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, " not", "preceded")); } return; } bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); bool precedesSpecialCharacter; if (!followedBySpace && !lastInLine) { SyntaxToken nextToken = token.GetNextToken(); precedesSpecialCharacter = nextToken.IsKind(SyntaxKind.CloseParenToken) || nextToken.IsKind(SyntaxKind.CommaToken) || nextToken.IsKind(SyntaxKind.SemicolonToken) || nextToken.IsKind(SyntaxKind.DotToken) || (nextToken.IsKind(SyntaxKind.QuestionToken) && nextToken.GetNextToken(includeZeroWidth: true).IsKind(SyntaxKind.DotToken)) || nextToken.IsKind(SyntaxKind.CloseBracketToken); } else { precedesSpecialCharacter = false; } if (!precededBySpace) { // Closing curly bracket must{} be {preceded} by a space. var properties = TokenSpacingCodeFixProvider.InsertPreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, string.Empty, "preceded")); } if (!lastInLine && !precedesSpecialCharacter && !followedBySpace) { // Closing curly bracket must{} be {followed} by a space. var properties = TokenSpacingCodeFixProvider.InsertFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, string.Empty, "followed")); } }
private static void AnalyzeTree(SyntaxTreeAnalysisContext context) { if (context.Tree.IsWhitespaceOnly(context.CancellationToken)) { // Handling of empty documents is now the responsibility of the analyzers return; } // Report a diagnostic if we got called context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Tree.GetLocation(TextSpan.FromBounds(0, 0)))); }
private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { SyntaxNode root; if (!context.Tree.TryGetRoot(out root)) return; IEnumerable<SyntaxTrivia> tabTrivias = root.DescendantTrivia(descendIntoTrivia: true) .Where(trivia => trivia.IsKind(SyntaxKind.WhitespaceTrivia) && trivia.ToString().IndexOf('\t') >= 0); foreach (SyntaxTrivia st in tabTrivias) { context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.IndentWithFourSpaces, st.GetLocation())); } }
private static async void AnalyzeSymbol(SyntaxTreeAnalysisContext context) { var cancel = context.CancellationToken; var tree = await context.Tree.GetRootAsync(cancel); var foreachs = tree.DescendantNodes().OfType<ForEachStatementSyntax>().ToList(); foreach (var fe in foreachs) { var loopVariableName = fe.Identifier.Text; IfStatementSyntax ifStatement; string ifType; if (TrySearchForIfThenContinueStatement(fe, out ifStatement, out ifType) || TrySearchForSingleIfStatement(fe, out ifStatement, out ifType)) { var ifExpression = ifStatement.Condition; var identifiersInExpression = ifExpression.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>(); var containsLoopVariable = identifiersInExpression.Any(x => x.Identifier.Text == loopVariableName); if (containsLoopVariable) { var properties = new Dictionary<string, string> { { RefactorType, ifType } }.ToImmutableDictionary(); context.ReportDiagnostic(Diagnostic.Create(Rule, ifStatement.Condition.GetLocation(), properties)); } } LocalDeclarationStatementSyntax assignmentStatement; if (TrySearchForVariableToSelect(fe, out assignmentStatement, loopVariableName)) { var declaratorsBasedOnLoopVariable = assignmentStatement.Declaration.Variables .Where(x => x.Initializer?.DescendantNodes()?.OfType<IdentifierNameSyntax>()?.Any(y => y.Identifier.Text == loopVariableName) ?? false); if (declaratorsBasedOnLoopVariable.Count() == 1) { var declarator = declaratorsBasedOnLoopVariable.Single(); var properties = new Dictionary<string, string> { { RefactorType, VariableToSelect } }.ToImmutableDictionary(); context.ReportDiagnostic(Diagnostic.Create(Rule, declarator.GetLocation(), properties)); } } } }
private static void HandleMultiLineComment(SyntaxTreeAnalysisContext context, SyntaxTrivia multiLineComment) { var nodeText = multiLineComment.ToString(); // We remove the /* and the */ and determine if the comment has any content. var commentText = nodeText.Substring(2, nodeText.Length - 4); if (string.IsNullOrWhiteSpace(commentText)) { var diagnostic = Diagnostic.Create(Descriptor, multiLineComment.GetLocation()); context.ReportDiagnostic(diagnostic); } }
private static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var typeDeclarations = syntaxRoot.DescendantNodes(IgnoreNodesInsideClassDeclarations).Where(NodeIsTypeDeclaration).ToList(); if (typeDeclarations.Count <= 1) { return; } foreach (var node in typeDeclarations) { var identifier = ((BaseTypeDeclarationSyntax)node).Identifier; context.ReportDiagnostic(Diagnostic.Create(Rule, identifier.GetLocation(), identifier.Text)); } }
private static void HandleCloseBraceToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace = token.IsFirstInLine() || token.IsPrecededByWhitespace(); if (token.Parent is InterpolationSyntax) { if (precededBySpace) { // Closing curly bracket must{ not} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), " not", "preceded")); } return; } bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); bool precedesSpecialCharacter; if (!followedBySpace && !lastInLine) { SyntaxToken nextToken = token.GetNextToken(); precedesSpecialCharacter = nextToken.IsKind(SyntaxKind.CloseParenToken) || nextToken.IsKind(SyntaxKind.CommaToken) || nextToken.IsKind(SyntaxKind.SemicolonToken); } else { precedesSpecialCharacter = false; } if (!precededBySpace) { // Closing curly bracket must{} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "preceded")); } if (!lastInLine && !precedesSpecialCharacter && !followedBySpace) { // Closing 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 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 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; } if (fileHeader.GetElement("copyright") == null) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, fileHeader.GetLocation(context.Tree))); } }
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.IsWhitespaceOnly(context.CancellationToken)) { // Handling of empty documents is now the responsibility of the analyzers return; } 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 AnalyzeTree(SyntaxTreeAnalysisContext context) { var root = context.Tree.GetRoot(); var allTrivia = root.DescendantTrivia(); var commentTriva = allTrivia.Where(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia) || x.IsKind(SyntaxKind.MultiLineCommentTrivia)).ToList(); foreach (var trivia in commentTriva) { //Only continue if the comment contains "sh!t" if (!trivia.ToString().Contains("sh!t")) continue; var diagnostic = Diagnostic.Create(Rule, trivia.GetLocation(), trivia.ToString()); context.ReportDiagnostic(diagnostic); } }
private static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.IsGenerated()) { return; } var leadingTrivia = context.Tree.GetRoot().GetLeadingTrivia(); if (!leadingTrivia.Any(x => x.IsKind(SyntaxKind.WhitespaceTrivia) || x.IsKind(SyntaxKind.EndOfLineTrivia))) { return; } var span = TextSpan.FromBounds(leadingTrivia.Span.Start, leadingTrivia.Span.End); var location = Location.Create(context.Tree, span); context.ReportDiagnostic(Diagnostic.Create(Rule, location)); }
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, assignment); else if (method.MethodKind == MethodKind.Constructor && !fieldSymbol.IsStatic) AddVariableThatWasSkippedBeforeBecauseItLackedAInitializer(variablesToMakeReadonly, fieldSymbol, assignment); 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); } } }
private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { var syntaxRoot = context.Tree.GetRoot(context.CancellationToken); var settings = _settingsHandler.GetArnolyzerSettingsForProject(context.Tree.FilePath); var classDeclarations = syntaxRoot.DescendantNodes(DoNotDescendIntoTypeDeclarations) .Where(NodeIsPublicClassDeclaration) .Cast<ClassDeclarationSyntax>().ToList(); var propertyDeclarations = from node in classDeclarations from property in node.DescendantNodes() .Where(NodeIsPropertyDeclaration) .Cast<PropertyDeclarationSyntax>() where SyntaxNodeIsPublic(property.Modifiers) && !AutoGenerated(property) && !IgnoredFile(property, settings) && !PropertyHasIgnoreRuleAttribute(property, SuppressionAttributes) select new { className = node.Identifier.Text, property }; foreach (var propertyDeclaration in propertyDeclarations) { propertyDeclaration.property.DescendantNodes() .Where(p => p.IsKind(SyntaxKind.SetAccessorDeclaration)) .Cast<AccessorDeclarationSyntax>() .Where(s => s.Modifiers.Count == 0) .TryFirst() .Match() .Some() .Do(setter => context.ReportDiagnostic( Diagnostic.Create(Rule, setter.Keyword.GetLocation(), propertyDeclaration.property.Identifier, propertyDeclaration.className))) .IgnoreElse() .Exec(); } }
private static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { if (context.Tree.IsGenerated()) { return; } var lastToken = context.Tree.GetRoot().GetLastToken(); var trailingTrivia = lastToken.TrailingTrivia; if (trailingTrivia.Count == 1 && trailingTrivia.First().IsKind(SyntaxKind.EndOfLineTrivia)) { return; } var span = TextSpan.FromBounds(lastToken.Span.Start, lastToken.Span.End); var location = Location.Create(context.Tree, span); context.ReportDiagnostic(Diagnostic.Create(Rule, location)); }
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")); } } }
private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace = token.IsFirstInLine() || token.IsPrecededByWhitespace(); 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: precedesStickyCharacter = true; 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 = true; break; case SyntaxKind.MinusToken: precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.UnaryMinusExpression); // this will be reported as SA1021 suppressFollowingSpaceError = true; 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; 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 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 (!suppressFollowingSpaceError) { if (!precedesStickyCharacter && !followedBySpace && !lastInLine) { // Closing parenthesis 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 (precedesStickyCharacter && followedBySpace && (!lastInLine || !allowEndOfLine)) { // Closing parenthesis 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> /// Creates a new instance of the <see cref="ContextualLoggableGeneratorDiagnosticReceiver{T}"/> class that accepts only <see cref="SyntaxTreeAnalysisContext"/>. /// </summary> /// <param name="generator">Target <see cref="LoggableSourceGenerator"/>.</param> /// <param name="context">Context of this <see cref="ContextualLoggableGeneratorDiagnosticReceiver{T}"/>.</param> public static ContextualLoggableGeneratorDiagnosticReceiver <SyntaxTreeAnalysisContext> SyntaxTree(LoggableSourceGenerator generator, SyntaxTreeAnalysisContext context) { return(new ContextualLoggableGeneratorDiagnosticReceiver <SyntaxTreeAnalysisContext>(generator, (context, diag) => context.ReportDiagnostic(diag), context)); }
private static void HandleAsteriskToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool allowAtLineStart; bool allowAtLineEnd; bool allowPrecedingSpace; bool allowTrailingSpace; switch (token.Parent.Kind()) { case SyntaxKind.PointerType: allowAtLineStart = false; allowAtLineEnd = true; allowPrecedingSpace = false; var nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: case SyntaxKind.AsteriskToken: allowTrailingSpace = false; break; default: allowTrailingSpace = true; break; } break; case SyntaxKind.PointerIndirectionExpression: allowAtLineStart = true; allowAtLineEnd = false; allowTrailingSpace = false; var prevToken = token.GetPreviousToken(); switch (prevToken.Kind()) { case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: allowPrecedingSpace = false; break; default: allowPrecedingSpace = true; break; } break; default: return; } bool firstInLine = token.IsFirstInLine(); bool precededBySpace = firstInLine || token.IsPrecededByWhitespace(); bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); if (!allowAtLineStart && firstInLine) { // Dereference symbol '*' must {not appear at the beginning of a line}. var properties = TokenSpacingCodeFixProvider.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, "not appear at the beginning of a line")); } else if (!allowPrecedingSpace && precededBySpace) { // Dereference symbol '*' must {not be preceded by a space}. var properties = TokenSpacingCodeFixProvider.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, "not be preceded by a space")); } if (!allowAtLineEnd && lastInLine) { // Dereference symbol '*' must {not appear at the end of a line}. var properties = TokenSpacingCodeFixProvider.RemoveFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, "not appear at the end of a line")); } else if (!allowTrailingSpace && followedBySpace) { // Dereference symbol '*' must {not be followed by a space}. var properties = TokenSpacingCodeFixProvider.RemoveFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, "not be followed by a space")); } if (!followedBySpace && allowTrailingSpace) { // Dereference symbol '*' must {be followed by a space}. var properties = TokenSpacingCodeFixProvider.InsertFollowing; context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, "be followed by a space")); } }
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; } }
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 void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { context.ReportDiagnostic(Diagnostic.Create( Descriptor, Location.Create(context.Tree, new TextSpan(0, 0)))); }
private void Analyze(SyntaxTreeAnalysisContext context) => context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, TextSpan.FromBounds(1000, 2000))));
private void HandleSemicolonToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { 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; } } else { SyntaxToken nextToken = token.GetNextToken(); if (nextToken.IsKind(SyntaxKind.CloseParenToken)) { // Special handling for the following case: // for (; ;) missingFollowingSpace = false; } } bool hasPrecedingSpace = false; bool ignorePrecedingSpace = false; if (!token.HasLeadingTrivia) { // only the first token on the line has leading trivia, and those are ignored SyntaxToken precedingToken = token.GetPreviousToken(); if (precedingToken.HasTrailingTrivia) { hasPrecedingSpace = true; } if (precedingToken.IsKind(SyntaxKind.SemicolonToken)) { // Special handling for the following case: // for (; ;) ignorePrecedingSpace = true; } } if (missingFollowingSpace) { // semicolon must{} be {followed} by a space context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "followed")); } if (hasPrecedingSpace && !ignorePrecedingSpace) { // semicolon must{ not} be {preceded} by a space context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), " not", "preceded")); } }
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(); 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: 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 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 (!allowTrailingNoSpace && !followedBySpace) { // Closing generic 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 (!allowTrailingSpace && followedBySpace) { // Closing generic bracket 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")); } } }
private void ProcessMemberDeclaration( SyntaxTreeAnalysisContext context, SyntaxGenerator generator, CodeStyleOption <AccessibilityModifiersRequired> option, MemberDeclarationSyntax member) { if (member.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax namespaceDeclaration)) { ProcessMembers(context, generator, 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, generator, 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 (!generator.CanHaveAccessibility(member)) { return; } // This analyzer bases all of its decisions on the accessibility var accessibility = generator.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)); }
/// <summary> /// Scans an entire document for lines with trailing whitespace. /// </summary> /// <param name="context">The context that provides the document to scan.</param> private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) { var root = context.Tree.GetRoot(context.CancellationToken); var text = root.GetText(); foreach (var trivia in root.DescendantTrivia(descendIntoTrivia: true)) { switch (trivia.Kind()) { case SyntaxKind.WhitespaceTrivia: bool reportWarning = false; var token = trivia.Token; SyntaxTriviaList triviaList; if (token.LeadingTrivia.Contains(trivia)) { triviaList = token.LeadingTrivia; } else { triviaList = token.TrailingTrivia; } bool foundWhitespace = false; foreach (var innerTrivia in triviaList) { if (!foundWhitespace) { if (innerTrivia.Equals(trivia)) { foundWhitespace = true; } continue; } if (innerTrivia.IsKind(SyntaxKind.EndOfLineTrivia)) { reportWarning = true; } break; } if (reportWarning) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, trivia.GetLocation())); } 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: case SyntaxKind.SingleLineDocumentationCommentTrivia: var line = text.Lines.GetLineFromPosition(trivia.Span.Start); do { 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); }while (line.End <= trivia.Span.End); break; case SyntaxKind.IfDirectiveTrivia: case SyntaxKind.ElifDirectiveTrivia: case SyntaxKind.ElseDirectiveTrivia: case SyntaxKind.EndIfDirectiveTrivia: case SyntaxKind.DefineDirectiveTrivia: case SyntaxKind.UndefDirectiveTrivia: case SyntaxKind.WarningDirectiveTrivia: case SyntaxKind.ErrorDirectiveTrivia: case SyntaxKind.RegionDirectiveTrivia: case SyntaxKind.EndRegionDirectiveTrivia: trailingWhitespace = FindTrailingWhitespace(text, trivia.Span); if (!trailingWhitespace.IsEmpty) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(context.Tree, trailingWhitespace))); } break; default: break; } } }
private void HandleOpenBraceToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace; bool firstInLine; bool allowLeadingSpace; bool allowLeadingNoSpace; bool followedBySpace; bool lastInLine; firstInLine = token.HasLeadingTrivia || token.GetLocation()?.GetMappedLineSpan().StartLinePosition.Character == 0; if (firstInLine) { precededBySpace = true; allowLeadingSpace = true; allowLeadingNoSpace = true; } else { SyntaxToken precedingToken = token.GetPreviousToken(); precededBySpace = precedingToken.HasTrailingTrivia; switch (precedingToken.Kind()) { case SyntaxKind.OpenParenToken: allowLeadingNoSpace = true; allowLeadingSpace = false; break; default: allowLeadingNoSpace = false; allowLeadingSpace = true; break; } } followedBySpace = token.HasTrailingTrivia; lastInLine = followedBySpace && token.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia); if (token.Parent is InterpolationSyntax) { // Don't report for interpolation string inlets return; } if (!firstInLine) { if (!allowLeadingSpace && precededBySpace) { // Opening curly bracket must{ not} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), " not", "preceded")); } else if (!allowLeadingNoSpace && !precededBySpace) { // Opening curly bracket must{} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "preceded")); } } if (!lastInLine && !followedBySpace) { // Opening curly bracket must{} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "followed")); } }
private void HandleAsteriskToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool allowAtLineStart; bool allowAtLineEnd; bool allowPrecedingSpace; bool allowPrecedingNoSpace; bool allowTrailingSpace; bool allowTrailingNoSpace; switch (token.Parent.Kind()) { case SyntaxKind.PointerType: allowAtLineStart = false; allowAtLineEnd = true; allowPrecedingNoSpace = true; allowPrecedingSpace = false; allowTrailingNoSpace = true; allowTrailingSpace = true; break; case SyntaxKind.PointerIndirectionExpression: allowAtLineStart = true; allowAtLineEnd = false; allowPrecedingNoSpace = true; allowPrecedingSpace = true; allowTrailingNoSpace = true; allowTrailingSpace = false; break; default: return; } bool precededBySpace; bool firstInLine; firstInLine = token.HasLeadingTrivia || token.GetLocation()?.GetMappedLineSpan().StartLinePosition.Character == 0; if (firstInLine) { precededBySpace = true; } else { SyntaxToken precedingToken = token.GetPreviousToken(); precededBySpace = precedingToken.HasTrailingTrivia; } bool followedBySpace = token.HasTrailingTrivia; bool lastInLine = followedBySpace && token.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia); if (!allowAtLineStart && firstInLine) { // Dereference symbol '*' must {not appear at the beginning of a line}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not appear at the beginning of a line")); } if (!allowAtLineEnd && lastInLine) { // Dereference symbol '*' must {not appear at the end of a line}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not appear at the end of a line")); } if (!allowPrecedingSpace && precededBySpace) { // Dereference symbol '*' must {not be preceded by a space}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not be preceded by a space")); } else if (!allowPrecedingNoSpace && !precededBySpace) { // Dereference symbol '*' must {be preceded by a space}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "be preceded by a space")); } if (!allowTrailingSpace && followedBySpace) { // Dereference symbol '*' must {not be followed by a space}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "not be followed by a space")); } else if (!allowTrailingNoSpace && !followedBySpace) { // Dereference symbol '*' must {be followed by a space}. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), "be followed by a space")); } }
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: 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); 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. ImmutableDictionary <string, string> properties; if (preserveLayout) { properties = TokenSpacingProperties.RemovePrecedingPreserveLayout; } else { 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")); } } }
public void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { context.ReportDiagnostic(Diagnostic.Create(s_syntaxDiagnosticDescriptor, context.Tree.GetRoot().GetFirstToken().GetLocation())); }
private static void HandleAsteriskToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool allowAtLineStart; bool allowAtLineEnd; bool allowPrecedingSpace; bool allowTrailingSpace; switch (token.Parent.Kind()) { case SyntaxKindEx.FunctionPointerType: allowAtLineStart = true; allowAtLineEnd = true; allowPrecedingSpace = false; var nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKindEx.ManagedKeyword: case SyntaxKindEx.UnmanagedKeyword: allowTrailingSpace = true; break; default: allowTrailingSpace = false; break; } break; case SyntaxKind.PointerType when token.Parent.Parent.IsKind(SyntaxKindEx.FunctionPointerParameter): allowAtLineStart = true; allowAtLineEnd = true; allowPrecedingSpace = false; allowTrailingSpace = false; break; case SyntaxKind.PointerType: allowAtLineStart = false; allowAtLineEnd = true; allowPrecedingSpace = false; nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: case SyntaxKind.AsteriskToken: allowTrailingSpace = false; break; default: allowTrailingSpace = true; break; } break; case SyntaxKind.PointerIndirectionExpression: allowAtLineStart = true; allowAtLineEnd = false; allowTrailingSpace = false; var prevToken = token.GetPreviousToken(); switch (prevToken.Kind()) { case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: allowPrecedingSpace = false; break; default: allowPrecedingSpace = true; break; } break; default: return; } bool firstInLine = token.IsFirstInLine(); bool precededBySpace = firstInLine || token.IsPrecededByWhitespace(context.CancellationToken); bool followedBySpace = token.IsFollowedByWhitespace(); bool lastInLine = token.IsLastInLine(); if (!allowAtLineStart && firstInLine) { // Dereference symbol '*' should {not appear at the beginning of a line}. var properties = TokenSpacingProperties.RemovePreceding; #pragma warning disable RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor (https://github.com/dotnet/roslyn-analyzers/issues/4103) context.ReportDiagnostic(Diagnostic.Create(DescriptorNotAtBeginningOfLine, token.GetLocation(), properties)); #pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor } else if (!allowPrecedingSpace && precededBySpace) { // Dereference symbol '*' should {not be preceded by a space}. var properties = TokenSpacingProperties.RemovePreceding; context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), properties)); } if (!allowAtLineEnd && lastInLine) { // Dereference symbol '*' should {not appear at the end of a line}. 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(DescriptorNotAtEndOfLine, token.GetLocation(), properties)); #pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor } else if (!allowTrailingSpace && followedBySpace) { // Dereference symbol '*' 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 } if (!followedBySpace && allowTrailingSpace) { // Dereference symbol '*' 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 } }
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(SyntaxTrivia); 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(SyntaxTrivia); } else { if (previousToken.TrailingTrivia.Count > 0) { precedingTrivia = previousToken.TrailingTrivia.Last(); } } } if (precedingTrivia.IsDirective) { DirectiveTriviaSyntax directiveTriviaSyntax = precedingTrivia.GetStructure() as DirectiveTriviaSyntax; if (directiveTriviaSyntax != null && 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))); }
/// <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) { if (context.Tree.IsWhitespaceOnly(context.CancellationToken)) { // Handling of empty documents is now the responsibility of the analyzers return; } var root = context.Tree.GetRoot(context.CancellationToken); var text = context.Tree.GetText(context.CancellationToken); SyntaxTrivia previousTrivia = default; 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; 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) { 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, ImmutableDictionary <string, ReportDiagnostic> specificDiagnosticOptions) { 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 (specificDiagnosticOptions.GetValueOrDefault(SA1507CodeMustNotContainMultipleBlankLinesInARow.DiagnosticId, ReportDiagnostic.Default) != ReportDiagnostic.Suppress) { // 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 AnalyzeTree(SyntaxTreeAnalysisContext context) { // Report a diagnostic if we got called context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Tree.GetLocation(TextSpan.FromBounds(0, 0)))); }
private static void HandlePlusToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } if (!token.Parent.IsKind(SyntaxKind.UnaryPlusExpression)) { return; } var isInInterpolationAlignmentClause = token.Parent.Parent.IsKind(SyntaxKind.InterpolationAlignmentClause); if (isInInterpolationAlignmentClause && !token.IsFollowedByWhitespace()) { // SA1001 is already handling the case like: line.Append($"{testResult.DisplayName, +75}"); // Where the extra space before the plus sign is undesirable. return; } bool precededBySpace = true; bool firstInLine = token.IsFirstInLine(); bool followsSpecialCharacter = false; bool followedBySpace = token.IsFollowedByWhitespace(); bool interpolatedUnaryExpression = token.IsInterpolatedUnaryExpression(); bool lastInLine = token.IsLastInLine(); if (!firstInLine) { precededBySpace = token.IsPrecededByWhitespace(context.CancellationToken); SyntaxToken precedingToken = token.GetPreviousToken(); followsSpecialCharacter = precedingToken.IsKind(SyntaxKind.OpenBracketToken) || precedingToken.IsKind(SyntaxKind.OpenParenToken) || precedingToken.IsKind(SyntaxKind.CloseParenToken) || (precedingToken.IsKind(SyntaxKind.OpenBraceToken) && interpolatedUnaryExpression); } if (!firstInLine && !isInInterpolationAlignmentClause) { if (!followsSpecialCharacter && !precededBySpace) { // Positive sign should{} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.InsertPreceding, string.Empty, "preceded")); } else if (followsSpecialCharacter && precededBySpace) { // Positive sign should{ not} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemovePreceding, " not", "preceded")); } } if (lastInLine || followedBySpace) { // Positive sign should{ not} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemoveFollowing, " not", "followed")); } }
public static void ReportDiagnosticWhenActive(this SyntaxTreeAnalysisContext context, Diagnostic diagnostic) { ReportWhenNotSuppressed(diagnostic, d => context.ReportDiagnostic(d)); }
private static void HandleSemicolonToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { 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; } } else { SyntaxToken nextToken = token.GetNextToken(); switch (nextToken.Kind()) { case SyntaxKind.CloseParenToken: // Special handling for the following case: // for (; ;) missingFollowingSpace = false; break; case SyntaxKind.SemicolonToken: // Special handling for the following case: // Statement();; if (nextToken.Parent.IsKind(SyntaxKind.EmptyStatement)) { missingFollowingSpace = false; } break; case SyntaxKind.None: // The semi colon is the last character in the file. return; default: break; } } bool hasPrecedingSpace = false; bool ignorePrecedingSpace = false; if (!token.IsFirstInLine()) { // only the first token on the line has leading trivia, and those are ignored SyntaxToken precedingToken = token.GetPreviousToken(); SyntaxTriviaList trailingTrivia = precedingToken.TrailingTrivia; if (trailingTrivia.Any() && trailingTrivia.Last().IsKind(SyntaxKind.WhitespaceTrivia)) { hasPrecedingSpace = true; } if (precedingToken.IsKind(SyntaxKind.SemicolonToken)) { // Special handling for the following case: // for (; ;) ignorePrecedingSpace = true; } } if (missingFollowingSpace) { // semicolon should{} be {followed} by a space context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.InsertFollowing, string.Empty, "followed")); } if (hasPrecedingSpace && !ignorePrecedingSpace) { // semicolon should{ not} be {preceded} by a space context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.RemoveImmediatePreceding, " not", "preceded")); } }
private void HandleDocumentationCommentExteriorTrivia(SyntaxTreeAnalysisContext context, SyntaxTrivia trivia) { SyntaxToken token = trivia.Token; if (token.IsMissing) { return; } switch (token.Kind()) { case SyntaxKind.EqualsToken: case SyntaxKind.DoubleQuoteToken: case SyntaxKind.SingleQuoteToken: case SyntaxKind.IdentifierToken: case SyntaxKind.GreaterThanToken: case SyntaxKind.SlashGreaterThanToken: case SyntaxKind.LessThanToken: case SyntaxKind.LessThanSlashToken: case SyntaxKind.XmlCommentStartToken: case SyntaxKind.XmlCommentEndToken: case SyntaxKind.XmlCDataStartToken: case SyntaxKind.XmlCDataEndToken: if (!token.HasLeadingTrivia) { break; } SyntaxTrivia lastLeadingTrivia = token.LeadingTrivia.Last(); switch (lastLeadingTrivia.Kind()) { case SyntaxKind.WhitespaceTrivia: if (lastLeadingTrivia.ToFullString().StartsWith(" ")) { return; } break; case SyntaxKind.DocumentationCommentExteriorTrivia: if (lastLeadingTrivia.ToFullString().EndsWith(" ")) { return; } break; default: break; } break; case SyntaxKind.EndOfDocumentationCommentToken: case SyntaxKind.XmlTextLiteralNewLineToken: return; case SyntaxKind.XmlTextLiteralToken: if (token.Text.StartsWith(" ")) { return; } else if (trivia.ToFullString().EndsWith(" ")) { // javadoc-style documentation comments without a leading * on one of the lines. return; } break; default: break; } // Documentation line must begin with a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation())); }
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(Descriptor, token.GetLocation(), properties, requireBefore ? string.Empty : " not", "preceded", string.Empty)); } if (missingFollowingSpace && checkRequireAfter) { // colon should{} be {followed}{} by a space context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), TokenSpacingProperties.InsertFollowing, string.Empty, "followed", string.Empty)); } }
private void AnalyzeTrailingTrivia(SyntaxTreeAnalysisContext context) { SourceText sourceText; if (!context.Tree.TryGetText(out sourceText)) { return; } SyntaxNode root; if (!context.Tree.TryGetRoot(out root)) { return; } var emptyLines = default(TextSpan); bool previousLineIsEmpty = false; int i = 0; foreach (TextLine textLine in sourceText.Lines) { bool lineIsEmpty = false; if (textLine.Span.Length == 0) { SyntaxTrivia endOfLine = root.FindTrivia(textLine.End); if (endOfLine.IsKind(SyntaxKind.EndOfLineTrivia)) { lineIsEmpty = true; if (previousLineIsEmpty) { if (emptyLines.IsEmpty) { emptyLines = endOfLine.Span; } else { emptyLines = TextSpan.FromBounds(emptyLines.Start, endOfLine.Span.End); } } } else { emptyLines = default(TextSpan); } } else { if (!emptyLines.IsEmpty) { context.ReportDiagnostic( DiagnosticDescriptors.RemoveRedundantEmptyLine, Location.Create(context.Tree, emptyLines)); } emptyLines = default(TextSpan); int end = textLine.End - 1; if (char.IsWhiteSpace(sourceText[end])) { int start = end; while (start > textLine.Span.Start && char.IsWhiteSpace(sourceText[start - 1])) { start--; } TextSpan whitespace = TextSpan.FromBounds(start, end + 1); if (root.FindTrivia(start).IsKind(SyntaxKind.WhitespaceTrivia)) { if (previousLineIsEmpty && start == textLine.Start) { whitespace = TextSpan.FromBounds( sourceText.Lines[i - 1].End, whitespace.End); } context.ReportDiagnostic( DiagnosticDescriptors.RemoveTrailingWhitespace, Location.Create(context.Tree, whitespace)); } } } previousLineIsEmpty = lineIsEmpty; i++; } }
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)) { if (haveLeadingSpace) { context.ReportDiagnostic(Diagnostic.Create(DescriptorPreceded, token.GetLocation(), OpenCloseSpacingCodeFixProvider.InsertPreceding)); } else { context.ReportDiagnostic(Diagnostic.Create(DescriptorNotPreceded, token.GetLocation(), OpenCloseSpacingCodeFixProvider.RemovePreceding)); } } } if (token.IsFollowedByWhitespace()) { context.ReportDiagnostic(Diagnostic.Create(DescriptorNotFollowed, token.GetLocation(), OpenCloseSpacingCodeFixProvider.RemoveFollowing)); } }
private void HandleCloseBraceToken(SyntaxTreeAnalysisContext context, SyntaxToken token) { if (token.IsMissing) { return; } bool precededBySpace; bool firstInLine; bool followedBySpace; bool lastInLine; bool precedesSpecialCharacter; firstInLine = token.HasLeadingTrivia || token.GetLocation()?.GetMappedLineSpan().StartLinePosition.Character == 0; if (firstInLine) { precededBySpace = true; } else { SyntaxToken precedingToken = token.GetPreviousToken(); precededBySpace = precedingToken.HasTrailingTrivia; } followedBySpace = token.HasTrailingTrivia; lastInLine = followedBySpace && token.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia); if (!followedBySpace && !lastInLine) { SyntaxToken nextToken = token.GetNextToken(); precedesSpecialCharacter = nextToken.IsKind(SyntaxKind.CloseParenToken) || nextToken.IsKind(SyntaxKind.CommaToken) || nextToken.IsKind(SyntaxKind.SemicolonToken); } else { precedesSpecialCharacter = false; } if (token.Parent is InterpolationSyntax) { // Don't report for interpolation string inlets return; } if (!firstInLine && !precededBySpace) { // Closing curly bracket must{} be {preceded} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "preceded")); } if (!lastInLine) { if (!precedesSpecialCharacter && !followedBySpace) { // Closing curly bracket must{} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), string.Empty, "followed")); } else if (precedesSpecialCharacter && followedBySpace) { // Closing curly bracket must{ not} be {followed} by a space. context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), " not", "followed")); } } }