/// <summary> /// Analyze the statements in the block for continuation lines. /// </summary> /// <param name="context">Analysis context.</param> private static void AnalyzeBlocks(SyntaxNodeAnalysisContext context) { // Grab the block syntax node. var block = (BlockSyntax)context.Node; // Ensure braces are on their own lines. CheckBraceLines( context, block.Parent, block.OpenBraceToken, block.CloseBraceToken); // Check each statement for continuation lines. foreach (var statement in block.Statements) { // Skip the flow control statements. These are expected to be multi-line and // don't require double-indent. if (statement.IsKind(SyntaxKind.IfStatement) || statement.IsKind(SyntaxKind.ForEachStatement) || statement.IsKind(SyntaxKind.ForStatement) || statement.IsKind(SyntaxKind.DoStatement) || statement.IsKind(SyntaxKind.SwitchStatement) || statement.IsKind(SyntaxKind.WhileStatement) || statement.IsKind(SyntaxKind.LockStatement) || statement.IsKind(SyntaxKind.UncheckedStatement) || statement.IsKind(SyntaxKind.UsingStatement) || statement.IsKind(SyntaxKind.Block)) { // Flow control statement. Skip. continue; } // There should be leading whitespace trivia. This includes the indent. if (!statement.HasLeadingTrivia) { continue; } // If this is a single-line statement, skip it. There will be no continuation line. var lines = statement.ToString().Split('\n'); if (lines.Length == 1) { continue; } // If the second line is opening brace, it should signal to the user that this is a // continuation statement and we can skip the indent rules. var secondLine = lines[1]; if (secondLine.Trim() == "{" || string.IsNullOrWhiteSpace(secondLine)) { continue; } // Get the whitespace preceding the statement. // There might be a lot of trivia preceding the statement. We're currently insterested only of // the whitespace on the last line. // First figure out where the last line begins from. var leadingTrivia = statement.GetLeadingTrivia().ToList(); var lastNewlineIndex = leadingTrivia.FindLastIndex( trivia => trivia.IsKind(SyntaxKind.EndOfLineTrivia)); int lastLineIndex = lastNewlineIndex == -1 ? 0 : lastNewlineIndex + 1; // Once we know where the last line starts, we can find the whitespace at the start of that line. var whitespaceOfLastLine = leadingTrivia.FindIndex( lastLineIndex, trivia => trivia.IsKind(SyntaxKind.WhitespaceTrivia)); // Calculate the expected indent for the second line. var firstLineIndent = whitespaceOfLastLine == -1 ? "" : leadingTrivia[whitespaceOfLastLine].ToString(); int firstIndent = SyntaxHelper.GetTextLength(firstLineIndent); var secondLineIndent = ALL_INDENT_REGEX.Match(secondLine).Value; int expectedIndent = firstIndent + 8; // Check whether the second line fulfills the indent requirement. if (SyntaxHelper.GetTextLength(secondLineIndent) < expectedIndent) { // Indent requirement not fulfilled. Report an issue. var start = statement.SpanStart + lines[0].Length + 1 + secondLineIndent.Length; var end = start + 1; var diagnostic = Diagnostic.Create( DoubleTabContinuationIndent.Rule, Location.Create(statement.SyntaxTree, TextSpan.FromBounds(start, end))); context.ReportDiagnostic(diagnostic); } } }
/// <summary> /// Analyze each line textually. /// </summary> /// <param name="context">Analysis context.</param> private static void AnalyzeLines(SyntaxTreeAnalysisContext context) { // Get the text for the file. var text = context.Tree.GetText(context.CancellationToken); // Gather non-CRLF lines. var nonCrlfLineEndings = new List <Location>(); // Check each line. foreach (var line in text.Lines) { // Chech whether the line stays withint he 120 character limit. var lineText = line.ToString(); int treshold; SyntaxHelper.GetTextLengthWith120Treshold(lineText, out treshold); if (treshold != -1) { // Line exceeds 120 characters. Report the error. var diagnostic = Diagnostic.Create( KeepLinesWithin120Characters.Rule, Location.Create(context.Tree, TextSpan.FromBounds(line.Span.Start + treshold, line.Span.End))); context.ReportDiagnostic(diagnostic); } // Check whether there are space indenting. var match = SPACE_INDENT_REGEX.Match(lineText); if (match.Success) { // Space indenting. REport error. var start = match.Groups["space"].Index; var end = start + match.Groups["space"].Length; var diagnostic = Diagnostic.Create( IndentWithTabs.Rule, Location.Create(context.Tree, TextSpan.FromBounds(line.Start + start, line.Start + end))); context.ReportDiagnostic(diagnostic); } // Check for trailing whitespace. var trailingMatch = TRAILING_WHITESPACE_REGEX.Match(lineText); if (trailingMatch.Success) { // Trailing whitespace. Report error. var diagnostic = Diagnostic.Create( NoTrailingWhitespace.Rule, Location.Create(context.Tree, TextSpan.FromBounds( line.Start + lineText.Length - trailingMatch.Length, line.End))); context.ReportDiagnostic(diagnostic); } // Skip the line ending check if this is the last line. // The last "line" has no line ending. if (line.End == context.Tree.Length) { continue; } // Ensure the line ends with CRLF. var expectedLineEndSpan = TextSpan.FromBounds( line.EndIncludingLineBreak - 2, line.EndIncludingLineBreak); var expectedLineEndText = line.Text.GetSubText(expectedLineEndSpan); var expectedLineEnd = expectedLineEndText.ToString(); if (expectedLineEnd != "\r\n") { // Non-CRLF line ending. var actualLineEndSpan = TextSpan.FromBounds(line.End, line.EndIncludingLineBreak); nonCrlfLineEndings.Add(Location.Create(context.Tree, actualLineEndSpan)); } } // If we had non-CRLF lines, report a diagnostic. // Do this only once per file to avoid spamming warnings. if (nonCrlfLineEndings.Count > 0) { // Non CRLF line endings. Report error. var firstLocation = nonCrlfLineEndings.First(); var additionalLocations = nonCrlfLineEndings.Skip(1); var diagnostic = Diagnostic.Create( UseWindowsLineEnding.Rule, firstLocation, additionalLocations); context.ReportDiagnostic(diagnostic); } }
/// <summary> /// Analyze the type names. /// </summary> /// <param name="context">Analysis context.</param> private static void AnalyzeTypeName(SymbolAnalysisContext context) { // Get the syntax bits referring to this symbol. var syntaxRefs = context.Symbol.DeclaringSyntaxReferences .Select(r => r.GetSyntax(context.CancellationToken)) .ToList(); // Check the type-specific rules. string unprefixedName = context.Symbol.Name; foreach (var syntax in syntaxRefs) { // Check for class rules. if (syntax.IsKind(SyntaxKind.ClassDeclaration)) { // Class. Check exception naming. var classSyntax = (ClassDeclarationSyntax)syntax; CheckExceptionName(context, classSyntax); // Check if the class has type parameters. if (classSyntax.TypeParameterList != null) { // Type parameters. Go through all. foreach (var typeParameter in classSyntax.TypeParameterList.Parameters) { AnalyzeTypeParameter(typeParameter, context.ReportDiagnostic); } } } // Check for interface rules. if (syntax.IsKind(SyntaxKind.InterfaceDeclaration)) { // Interface. Save for I prefix. var interfaceSyntax = ( InterfaceDeclarationSyntax )syntax; // If we encounter this as an interface, get the unprefixed name from that. unprefixedName = CheckPrefix( "I", interfaceSyntax.Identifier, NameInterfacesWithIPrefix, IsPascalCase, context.ReportDiagnostic); // Check if the interface has type parameters. if (interfaceSyntax.TypeParameterList != null) { // Type parameters. Go through all. foreach (var typeParameter in interfaceSyntax.TypeParameterList.Parameters) { AnalyzeTypeParameter(typeParameter, context.ReportDiagnostic); } } } // If this is a top-level type, we'll check for file naming. if (syntax.Parent.IsKind(SyntaxKind.NamespaceDeclaration)) { // Resolve the file information. string file = syntax.SyntaxTree.FilePath; string filename = Path.GetFileNameWithoutExtension(file); // Get the location for the declaration for more specific target. // Try to find the identifier, otherwise use the whole block. Location declarationLocation = GetDeclarationLocation(syntax); // Check the file name matches the symbol name. if (context.Symbol.Name != filename && IsHelperToSibling(syntax) == false) { // File name doesn't match the symbol. Report the issue. var diagnostic = Diagnostic.Create( NameFilesAccordingToTypeNames.Rule, declarationLocation, context.Symbol.Name, context.Symbol.Name + ".cs"); context.ReportDiagnostic(diagnostic); } } } // Check the naming. CheckName( context, unprefixedName, NameTypesWithPascalCasing, IsPascalCase, SyntaxHelper.GetItemType(syntaxRefs.First())); }