/// <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);
            }
        }
Пример #3
0
        /// <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()));
        }