private static TextChange FixDiagnostic(IndentationSettings indentationSettings, SourceText sourceText, Diagnostic diagnostic)
        {
            TextSpan span = diagnostic.Location.SourceSpan;

            TextLine startLine = sourceText.Lines.GetLineFromPosition(span.Start);

            bool   useTabs = false;
            string behavior;

            if (diagnostic.Properties.TryGetValue(SA1027UseTabsCorrectly.BehaviorKey, out behavior))
            {
                useTabs = behavior == SA1027UseTabsCorrectly.ConvertToTabsBehavior;
            }

            string        text        = sourceText.ToString(TextSpan.FromBounds(startLine.Start, span.End));
            StringBuilder replacement = StringBuilderPool.Allocate();
            int           spaceCount  = 0;
            int           column      = 0;

            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                if (c == '\t')
                {
                    var offsetWithinTabColumn = column % indentationSettings.TabSize;
                    var tabWidth = indentationSettings.TabSize - offsetWithinTabColumn;

                    if (i >= span.Start - startLine.Start)
                    {
                        if (useTabs)
                        {
                            replacement.Length = replacement.Length - spaceCount;
                            replacement.Append('\t');
                            spaceCount = 0;
                        }
                        else
                        {
                            replacement.Append(' ', tabWidth);
                        }
                    }

                    column += tabWidth;
                }
                else
                {
                    if (i >= span.Start - startLine.Start)
                    {
                        replacement.Append(c);
                        if (c == ' ')
                        {
                            spaceCount++;
                            if (useTabs)
                            {
                                // Note that we account for column not yet being incremented
                                var offsetWithinTabColumn = (column + 1) % indentationSettings.TabSize;
                                if (offsetWithinTabColumn == 0)
                                {
                                    // We reached a tab stop.
                                    replacement.Length = replacement.Length - spaceCount;
                                    replacement.Append('\t');
                                    spaceCount = 0;
                                }
                            }
                        }
                        else
                        {
                            spaceCount = 0;
                        }
                    }

                    if (c == '\r' || c == '\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.
                        column     = 0;
                        spaceCount = 0;
                    }
                    else
                    {
                        column++;
                    }
                }
            }

            return(new TextChange(span, StringBuilderPool.ReturnAndFree(replacement)));
        }
示例#2
0
        protected static void AnalyzeWithRule <T>(Func <string, SyntaxTree> parseTextFunc, Func <SyntaxTree[], Compilation> createCompilationFunc, string language, string input, string ruleId, string output = null, int issueToFix = -1, int actionToRun = 0, Action <int, Diagnostic> diagnosticCheck = null) where T : DiagnosticAnalyzer, new()
        {
            var text = new StringBuilder();

            var expectedDiagnosics = new List <TextSpan>();
            int start = -1;

            for (int i = 0; i < input.Length; i++)
            {
                char ch = input[i];
                if (ch == '$')
                {
                    if (start < 0)
                    {
                        start = text.Length;
                        continue;
                    }
                    expectedDiagnosics.Add(TextSpan.FromBounds(start, text.Length));
                    start = -1;
                }
                else
                {
                    text.Append(ch);
                }
            }

            var syntaxTree = parseTextFunc(text.ToString());

            Compilation compilation = createCompilationFunc(new[] { syntaxTree });

            var diagnostics = new List <Diagnostic>();
            var compilationWithAnalyzers = compilation.WithAnalyzers(System.Collections.Immutable.ImmutableArray <DiagnosticAnalyzer> .Empty.Add(new T()));

            diagnostics.AddRange(compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().GetAwaiter().GetResult());


            if (expectedDiagnosics.Count != diagnostics.Count)
            {
                Console.WriteLine("Diagnostics: " + diagnostics.Count);
                foreach (var diag in diagnostics)
                {
                    Console.WriteLine(diag.Id + "/" + diag.GetMessage());
                }
                Assert.True(false, "Diagnostic count mismatch expected: " + expectedDiagnosics.Count + " but was:" + diagnostics.Count);
            }

            for (int i = 0; i < expectedDiagnosics.Count; i++)
            {
                var d         = diagnostics[i];
                var wholeSpan = GetWholeSpan(d);
                if (wholeSpan != expectedDiagnosics[i])
                {
                    Assert.True(false, "Diagnostic " + i + " span mismatch expected: " + expectedDiagnosics[i] + " but was " + wholeSpan);
                }
                if (diagnosticCheck != null)
                {
                    diagnosticCheck(i, d);
                }
            }

            if (output == null)
            {
                return;
            }

            var workspace  = new TestWorkspace();
            var projectId  = ProjectId.CreateNewId();
            var documentId = DocumentId.CreateNewId(projectId);

            workspace.Open(ProjectInfo.Create(
                               projectId,
                               VersionStamp.Create(),
                               "", "", language, null, null, null, null,
                               new[] {
                DocumentInfo.Create(
                    documentId,
                    "a.cs",
                    null,
                    SourceCodeKind.Regular,
                    TextLoader.From(TextAndVersion.Create(SourceText.From(text.ToString()), VersionStamp.Create())))
            }
                               ));
            if (issueToFix < 0)
            {
                diagnostics.Reverse();
                foreach (var v in diagnostics)
                {
                    RunFix(workspace, projectId, documentId, v);
                }
            }
            else
            {
                RunFix(workspace, projectId, documentId, diagnostics.ElementAt(issueToFix), actionToRun);
            }

            var txt = workspace.CurrentSolution.GetProject(projectId).GetDocument(documentId).GetTextAsync().GetAwaiter().GetResult().ToString();

            txt    = Utils.HomogenizeEol(txt);
            output = Utils.HomogenizeEol(output);
            if (output != txt)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("expected:");
                sb.AppendLine(output);
                sb.AppendLine("got:");
                sb.AppendLine(txt);
                Assert.True(false, sb.ToString());
            }
        }
示例#3
0
        public static (string source, List <Diagnostic> diagnostics) GetMarkedDiagnostics(
            string s,
            DiagnosticDescriptor descriptor,
            string filePath)
        {
            StringBuilder sb = StringBuilderCache.GetInstance(s.Length - OpenMarker.Length - CloseMarker.Length);

            List <Diagnostic> diagnostics = null;

            int lastPos = 0;

            int line   = 0;
            int column = 0;

            int startLine   = -1;
            int startColumn = -1;

            int length = s.Length;

            for (int i = 0; i < length; i++)
            {
                switch (s[i])
                {
                case '\r':
                {
                    if (i < length - 1 &&
                        s[i + 1] == '\n')
                    {
                        i++;
                    }

                    line++;
                    column = 0;
                    continue;
                }

                case '\n':
                {
                    line++;
                    column = 0;
                    continue;
                }

                case '<':
                {
                    if (i < length - 1 &&
                        IsOpenMarker(s, length, i))
                    {
                        sb.Append(s, lastPos, i - lastPos);

                        startLine   = line;
                        startColumn = column;

                        i += 2;

                        lastPos = i + 1;

                        continue;
                    }

                    break;
                }

                case '>':
                {
                    if (startColumn != -1 &&
                        IsCloseMarker(s, length, i))
                    {
                        sb.Append(s, lastPos, i - lastPos);

                        var lineSpan = new LinePositionSpan(
                            new LinePosition(startLine, startColumn),
                            new LinePosition(line, column));

                        TextSpan span = TextSpan.FromBounds(lastPos, i);

                        Location location = Location.Create(filePath, span, lineSpan);

                        Diagnostic diagnostic = Diagnostic.Create(descriptor, location);

                        (diagnostics ?? (diagnostics = new List <Diagnostic>())).Add(diagnostic);

                        i += 2;

                        lastPos = i + 1;

                        startLine   = -1;
                        startColumn = -1;

                        continue;
                    }

                    break;
                }
                }

                column++;
            }

            sb.Append(s, lastPos, s.Length - lastPos);

            return(StringBuilderCache.GetStringAndFree(sb), diagnostics);
        }
示例#4
0
 internal static TextSpan TrailingTriviaSpan(this SyntaxTrivia trivia)
 {
     return(TextSpan.FromBounds(trivia.Span.End, trivia.FullSpan.End));
 }
示例#5
0
            private void AppendTextSpan(int spanStartIndex, int spanEndIndex)
            {
                int shift = textSpans.Count * (SpanTextLength + SpanTextLength);

                textSpans.Add(TextSpan.FromBounds(spanStartIndex - shift, spanEndIndex - SpanTextLength - shift));
            }
 /// <summary>
 /// Convert a <see cref="LinePositionSpan"/> to <see cref="TextSpan"/>.
 /// </summary>
 public static TextSpan GetTextSpan(this ITextSnapshot snapshot, LinePositionSpan span)
 {
     return(TextSpan.FromBounds(
                GetPosition(snapshot, span.Start.Line, span.Start.Character),
                GetPosition(snapshot, span.End.Line, span.End.Character)));
 }
示例#7
0
 internal static TextSpan TrailingTriviaSpan(this SyntaxToken token)
 {
     return(TextSpan.FromBounds(token.Span.End, token.FullSpan.End));
 }
 public RelativeIndentationData(int inseparableRegionSpanStart, TextSpan textSpan, IndentBlockOperation operation, Lazy <int> indentationGetter)
     : base(textSpan, indentationGetter)
 {
     this.Operation             = operation;
     this.InseparableRegionSpan = TextSpan.FromBounds(inseparableRegionSpanStart, textSpan.End);
 }
        static Location GetLocation(InvocationExpressionSyntax invocationExpression)
        {
            var memberExpression = invocationExpression.Expression as MemberAccessExpressionSyntax;

            return(Location.Create(invocationExpression.SyntaxTree, TextSpan.FromBounds(memberExpression.Expression.Span.End, invocationExpression.Span.End)));
        }
 private void AnalyzeTree(SyntaxTreeAnalysisContext context)
 {
     // Report a diagnostic if we got called
     context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Tree.GetLocation(TextSpan.FromBounds(0, 0))));
 }
        private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol expressionTypeOpt)
        {
            var syntaxTree = context.Operation.Syntax.SyntaxTree;

            if (!IsSupported(syntaxTree.Options))
            {
                return;
            }

            var cancellationToken = context.CancellationToken;

            var throwOperation = (IThrowStatement)context.Operation;
            var throwStatement = throwOperation.Syntax;
            var options        = context.Options;
            var optionSet      = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();

            if (optionSet == null)
            {
                return;
            }

            var option = optionSet.GetOption(CodeStyleOptions.PreferThrowExpression, throwStatement.Language);

            if (!option.Value)
            {
                return;
            }

            var compilation   = context.Compilation;
            var semanticModel = compilation.GetSemanticModel(throwStatement.SyntaxTree);

            if (IsInExpressionTree(throwStatement, semanticModel, expressionTypeOpt, cancellationToken))
            {
                return;
            }

            var ifOperation = GetContainingIfOperation(
                semanticModel, throwOperation, cancellationToken);

            // This throw statement isn't parented by an if-statement.  Nothing to
            // do here.
            if (ifOperation == null)
            {
                return;
            }

            var containingBlock = GetOperation(
                semanticModel, ifOperation.Syntax.Parent, cancellationToken) as IBlockStatement;

            if (containingBlock == null)
            {
                return;
            }

            if (!TryDecomposeIfCondition(ifOperation, out var localOrParameter))
            {
                return;
            }

            if (!TryFindAssignmentExpression(containingBlock, ifOperation, localOrParameter,
                                             out var expressionStatement, out var assignmentExpression))
            {
                return;
            }

            // We found an assignment using this local/parameter.  Now, just make sure there
            // were no intervening accesses between the check and the assignment.
            var statements               = containingBlock.Statements;
            var ifOperationIndex         = statements.IndexOf(ifOperation);
            var expressionStatementIndex = statements.IndexOf(expressionStatement);

            if (expressionStatementIndex > ifOperationIndex + 1)
            {
                // There are intermediary statements between the check and the assignment.
                // Make sure they don't try to access the local.
                var dataFlow = semanticModel.AnalyzeDataFlow(
                    statements[ifOperationIndex + 1].Syntax,
                    statements[expressionStatementIndex - 1].Syntax);

                if (dataFlow.ReadInside.Contains(localOrParameter) ||
                    dataFlow.WrittenInside.Contains(localOrParameter))
                {
                    return;
                }
            }

            // Ok, there were no intervening writes or accesses.  This check+assignment can be simplified.

            var allLocations = ImmutableArray.Create(
                ifOperation.Syntax.GetLocation(),
                throwOperation.ThrownObject.Syntax.GetLocation(),
                assignmentExpression.Value.Syntax.GetLocation());

            var descriptor = GetDescriptorWithSeverity(option.Notification.Value);

            context.ReportDiagnostic(
                Diagnostic.Create(descriptor, throwStatement.GetLocation(), additionalLocations: allLocations));

            // Fade out the rest of the if that surrounds the 'throw' exception.

            var tokenBeforeThrow = throwStatement.GetFirstToken().GetPreviousToken();
            var tokenAfterThrow  = throwStatement.GetLastToken().GetNextToken();

            context.ReportDiagnostic(
                Diagnostic.Create(UnnecessaryWithSuggestionDescriptor,
                                  Location.Create(syntaxTree, TextSpan.FromBounds(
                                                      ifOperation.Syntax.SpanStart,
                                                      tokenBeforeThrow.Span.End)),
                                  additionalLocations: allLocations));

            if (ifOperation.Syntax.Span.End > tokenAfterThrow.Span.Start)
            {
                context.ReportDiagnostic(
                    Diagnostic.Create(UnnecessaryWithSuggestionDescriptor,
                                      Location.Create(syntaxTree, TextSpan.FromBounds(
                                                          tokenAfterThrow.Span.Start,
                                                          ifOperation.Syntax.Span.End)),
                                      additionalLocations: allLocations));
            }
        }
示例#12
0
        protected override void Initialize(SonarAnalysisContext context)
        {
            context.RegisterSyntaxNodeActionInNonGenerated(
                c =>
            {
                var node = (OnErrorGoToStatementSyntax)c.Node;
                c.ReportDiagnostic(Diagnostic.Create(rule,
                                                     Location.Create(node.SyntaxTree, TextSpan.FromBounds(node.OnKeyword.SpanStart, node.ErrorKeyword.Span.End))));
            },
                SyntaxKind.OnErrorGoToLabelStatement,
                SyntaxKind.OnErrorGoToZeroStatement,
                SyntaxKind.OnErrorGoToMinusOneStatement);

            context.RegisterSyntaxNodeActionInNonGenerated(
                c =>
            {
                var node = (OnErrorResumeNextStatementSyntax)c.Node;
                c.ReportDiagnostic(Diagnostic.Create(rule,
                                                     Location.Create(node.SyntaxTree, TextSpan.FromBounds(node.OnKeyword.SpanStart, node.ErrorKeyword.Span.End))));
            },
                SyntaxKind.OnErrorResumeNextStatement);
        }
示例#13
0
        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)));
            }
        }
 public override void Initialize(AnalysisContext context)
 {
     context.RegisterSyntaxTreeAction(c =>
     {
         c.ReportDiagnostic(Diagnostic.Create(_supportedDiagnostics[0], c.Tree.GetLocation(TextSpan.FromBounds(0, 1))));
     });
 }
    /// <summary>
    /// Formats the snippet by applying the snippet to the document with the default values / function results for snippet declarations.
    /// Then converts back into an LSP snippet by replacing the declarations with the appropriate LSP tab stops.
    ///
    /// Note that the operations in this method are sensitive to the context in the document and so must be calculated on each request.
    /// </summary>
    private static async Task <string> GetFormattedLspSnippetAsync(
        ParsedXmlSnippet parsedSnippet,
        TextSpan snippetShortcut,
        Document originalDocument,
        SourceText originalSourceText,
        SyntaxFormattingOptions formattingOptions,
        SimplifierOptions simplifierOptions,
        CancellationToken cancellationToken)
    {
        // Calculate the snippet text with defaults + snippet function results.
        var(snippetFullText, fields, caretSpan) = await GetReplacedSnippetTextAsync(
            originalDocument, originalSourceText, snippetShortcut, parsedSnippet, simplifierOptions, cancellationToken).ConfigureAwait(false);

        // Create a document with the default snippet text that we can use to format the snippet.
        var textChange         = new TextChange(snippetShortcut, snippetFullText);
        var snippetEndPosition = textChange.Span.Start + textChange.NewText !.Length;

        var documentWithSnippetText = originalSourceText.WithChanges(textChange);
        var root = await originalDocument.WithText(documentWithSnippetText).GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

        var spanToFormat      = TextSpan.FromBounds(textChange.Span.Start, snippetEndPosition);
        var formattingChanges = Formatter.GetFormattedTextChanges(root, spanToFormat, originalDocument.Project.Solution.Workspace.Services, formattingOptions, cancellationToken: cancellationToken)
                                ?.ToImmutableArray() ?? ImmutableArray <TextChange> .Empty;

        var formattedText = documentWithSnippetText.WithChanges(formattingChanges);

        // We now have a formatted snippet with default values.  We need to
        // replace the fields and caret with the proper LSP tab stop notation.
        // Since formatting changes are entirely whitespace, we can calculate the new locations by
        // adjusting the old spans based on the formatting changes that occured before them.

        // Get the adjusted snippet bounds.
        snippetEndPosition = GetAdjustedSpan(formattingChanges, new TextSpan(snippetEndPosition, 0)).Start;
        var spanContainingFormattedSnippet = TextSpan.FromBounds(snippetShortcut.Start, snippetEndPosition);

        // Get the adjusted fields and determine the text edits to make LSP formatted tab stops.
        using var _1 = ArrayBuilder <TextChange> .GetInstance(out var lspTextChanges);

        foreach (var(field, spans) in fields)
        {
            var lspTextForField = string.IsNullOrEmpty(field.DefaultText) ? $"${{{field.EditIndex}}}" : $"${{{field.EditIndex}:{field.DefaultText}}}";
            foreach (var span in spans)
            {
                // Adjust the span based on the formatting changes and build the snippet text change.
                var fieldInFormattedText  = GetAdjustedSpan(formattingChanges, span);
                var fieldInSnippetContext = GetTextSpanInContextOfSnippet(fieldInFormattedText.Start, spanContainingFormattedSnippet.Start, fieldInFormattedText.Length);
                lspTextChanges.Add(new TextChange(fieldInSnippetContext, lspTextForField));
            }
        }

        // Get the adjusted caret location and replace the placeholder comment with the LSP formatted tab stop.
        if (caretSpan != null)
        {
            var caretInFormattedText  = GetAdjustedSpan(formattingChanges, caretSpan.Value);
            var caretInSnippetContext = GetTextSpanInContextOfSnippet(caretInFormattedText.Start, spanContainingFormattedSnippet.Start, caretInFormattedText.Length);
            lspTextChanges.Add(new TextChange(caretInSnippetContext, "$0"));
        }

        // Apply all the text changes to get the text formatted as the LSP snippet syntax.
        var formattedLspSnippetText = formattedText.GetSubText(spanContainingFormattedSnippet).WithChanges(lspTextChanges);

        return(formattedLspSnippetText.ToString());
示例#16
0
        internal Task <CodeActionContainer> GetCurrentFixesAsync(CancellationToken cancellationToken)
        {
            var loc = Editor.CaretOffset;
            var ad  = DocumentContext.AnalysisDocument;

            if (ad == null)
            {
                return(Task.FromResult(CodeActionContainer.Empty));
            }
            TextSpan span;

            if (Editor.IsSomethingSelected)
            {
                var selectionRange = Editor.SelectionRange;
                span = selectionRange.Offset >= 0 ? TextSpan.FromBounds(selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds(loc, loc);
            }
            else
            {
                span = TextSpan.FromBounds(loc, loc);
            }
            var rExt      = Editor.GetContent <ResultsEditorExtension> ();
            var errorList = Editor
                            .GetTextSegmentMarkersAt(Editor.CaretOffset)
                            .OfType <IErrorMarker> ()
                            .Where(rm => !string.IsNullOrEmpty(rm.Error.Id)).ToList();

            return(Task.Run(async delegate {
                try {
                    var result = await CodeDiagnosticRunner.Check(new AnalysisDocument(Editor, DocumentContext), cancellationToken).ConfigureAwait(false);
                    var diagnosticsAtCaret = result.OfType <DiagnosticResult> ().Where(d => d.Region.Contains(loc)).Select(d => d.Diagnostic).ToList();

                    var codeIssueFixes = new List <ValidCodeDiagnosticAction> ();
                    var diagnosticIds = diagnosticsAtCaret.Select(diagnostic => diagnostic.Id).Concat(errorList.Select(rm => rm.Error.Id)).ToList();
                    if (codeFixes == null)
                    {
                        codeFixes = (await CodeRefactoringService.GetCodeFixesAsync(DocumentContext, CodeRefactoringService.MimeTypeToLanguage(Editor.MimeType), cancellationToken).ConfigureAwait(false)).ToList();
                    }
                    var root = await ad.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                    foreach (var cfp in codeFixes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return CodeActionContainer.Empty;
                        }
                        var provider = cfp.GetCodeFixProvider();
                        if (!provider.FixableDiagnosticIds.Any(diagnosticIds.Contains))
                        {
                            continue;
                        }

                        // These two delegates were factored out, as using them as lambdas in the inner loop creates more captures than declaring them here.
                        Func <Diagnostic, bool> providerIdsContain = d => provider.FixableDiagnosticIds.Contains(d.Id);
                        Action <Microsoft.CodeAnalysis.CodeActions.CodeAction, ImmutableArray <Diagnostic> > codeFixRegistration = (ca, d) => codeIssueFixes.Add(new ValidCodeDiagnosticAction(cfp, ca, d, d [0].Location.SourceSpan));
                        try {
                            var groupedDiagnostics = diagnosticsAtCaret
                                                     .Concat(errorList.Select(em => em.Error.Tag)
                                                             .OfType <Diagnostic> ())
                                                     .GroupBy(d => d.Location.SourceSpan);
                            foreach (var g in groupedDiagnostics)
                            {
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    return CodeActionContainer.Empty;
                                }
                                var diagnosticSpan = g.Key;
                                var validDiagnostics = g.Where(providerIdsContain).ToImmutableArray();
                                if (validDiagnostics.Length == 0)
                                {
                                    continue;
                                }
                                if (diagnosticSpan.Start < 0 || diagnosticSpan.End > root.Span.End)
                                {
                                    continue;
                                }
                                await provider.RegisterCodeFixesAsync(new CodeFixContext(ad, diagnosticSpan, validDiagnostics, codeFixRegistration, cancellationToken)).ConfigureAwait(false);

                                // TODO: Is that right ? Currently it doesn't really make sense to run one code fix provider on several overlapping diagnostics at the same location
                                //       However the generate constructor one has that case and if I run it twice the same code action is generated twice. So there is a dupe check problem there.
                                // Work around for now is to only take the first diagnostic batch.
                                break;
                            }
                        } catch (OperationCanceledException) {
                            return CodeActionContainer.Empty;
                        } catch (AggregateException ae) {
                            ae.Flatten().Handle(aex => aex is OperationCanceledException);
                            return CodeActionContainer.Empty;
                        } catch (Exception ex) {
                            LoggingService.LogError("Error while getting refactorings from code fix provider " + cfp.Name, ex);
                            continue;
                        }
                    }
                    var codeActions = new List <ValidCodeAction> ();
                    foreach (var action in await CodeRefactoringService.GetValidActionsAsync(Editor, DocumentContext, span, cancellationToken).ConfigureAwait(false))
                    {
                        codeActions.Add(action);
                    }
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return CodeActionContainer.Empty;
                    }

                    var codeActionContainer = new CodeActionContainer(codeIssueFixes, codeActions, diagnosticsAtCaret);
                    Application.Invoke((o, args) => {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return;
                        }
                        if (codeActionContainer.IsEmpty)
                        {
                            RemoveWidget();
                            return;
                        }
                        CreateSmartTag(codeActionContainer, loc);
                    });
                    return codeActionContainer;
                } catch (AggregateException ae) {
                    ae.Flatten().Handle(aex => aex is OperationCanceledException);
                    return CodeActionContainer.Empty;
                } catch (OperationCanceledException) {
                    return CodeActionContainer.Empty;
                } catch (TargetInvocationException ex) {
                    if (ex.InnerException is OperationCanceledException)
                    {
                        return CodeActionContainer.Empty;
                    }
                    throw;
                }
            }, cancellationToken));
        }
        private async Task <Document> GenerateMemberAndUsingsAsync(
            Document document,
            CompletionItem completionItem,
            TextLine line,
            CancellationToken cancellationToken)
        {
            var syntaxFactory  = document.GetLanguageService <SyntaxGenerator>();
            var codeGenService = document.GetLanguageService <ICodeGenerationService>();

            // Resolve member and type in our new, forked, solution
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var containingType = semanticModel.GetEnclosingSymbol <INamedTypeSymbol>(line.Start, cancellationToken);
            var symbols        = await SymbolCompletionItem.GetSymbolsAsync(completionItem, document, cancellationToken).ConfigureAwait(false);

            var overriddenMember = symbols.FirstOrDefault();

            if (overriddenMember == null)
            {
                // Unfortunately, SymbolKey resolution failed. Bail.
                return(null);
            }

            // CodeGenerationOptions containing before and after
            var options = new CodeGenerationOptions(contextLocation: semanticModel.SyntaxTree.GetLocation(TextSpan.FromBounds(line.Start, line.Start)));

            var generatedMember = await GenerateMemberAsync(overriddenMember, containingType, document, completionItem, cancellationToken).ConfigureAwait(false);

            generatedMember = _annotation.AddAnnotationToSymbol(generatedMember);

            Document memberContainingDocument = null;

            if (generatedMember.Kind == SymbolKind.Method)
            {
                memberContainingDocument = await codeGenService.AddMethodAsync(document.Project.Solution, containingType, (IMethodSymbol)generatedMember, options, cancellationToken).ConfigureAwait(false);
            }
            else if (generatedMember.Kind == SymbolKind.Property)
            {
                memberContainingDocument = await codeGenService.AddPropertyAsync(document.Project.Solution, containingType, (IPropertySymbol)generatedMember, options, cancellationToken).ConfigureAwait(false);
            }
            else if (generatedMember.Kind == SymbolKind.Event)
            {
                memberContainingDocument = await codeGenService.AddEventAsync(document.Project.Solution, containingType, (IEventSymbol)generatedMember, options, cancellationToken).ConfigureAwait(false);
            }

            return(memberContainingDocument);
        }
示例#18
0
        public static async Task <Document> RefactorAsync(
            Document document,
            UseMethodChainingAnalysis analysis,
            ExpressionStatementSyntax expressionStatement,
            CancellationToken cancellationToken)
        {
            SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            InvocationExpressionSyntax invocationExpression = GetInvocationExpression(expressionStatement);

            SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression);

            ITypeSymbol returnType = semanticModel.GetMethodSymbol(invocationExpression, cancellationToken).ReturnType;

            string name = ((IdentifierNameSyntax)UseMethodChainingAnalysis.WalkDownMethodChain(invocationInfo).Expression).Identifier.ValueText;

            StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(expressionStatement);

            SyntaxList <StatementSyntax> statements = statementsInfo.Statements;

            int index = statements.IndexOf(expressionStatement);

            string indentation = expressionStatement.GetIncreasedIndentation(cancellationToken).ToString();

            var sb = new StringBuilder(invocationExpression.ToString());

            int j = index;

            while (j < statements.Count - 1)
            {
                StatementSyntax statement = statements[j + 1];

                if (!analysis.IsFixableStatement(statement, name, returnType, semanticModel, cancellationToken))
                {
                    break;
                }

                sb.AppendLine();
                sb.Append(indentation);
                sb.Append(GetTextToAppend((ExpressionStatementSyntax)statement));

                j++;
            }

            StatementSyntax lastStatement = statements[j];

            SyntaxList <StatementSyntax> newStatements = statements;

            while (j > index)
            {
                newStatements = newStatements.RemoveAt(j);
                j--;
            }

            ExpressionSyntax newInvocationExpression = SyntaxFactory.ParseExpression(sb.ToString());

            SyntaxTriviaList trailingTrivia = statementsInfo
                                              .Parent
                                              .DescendantTrivia(TextSpan.FromBounds(invocationExpression.Span.End, lastStatement.Span.End))
                                              .ToSyntaxTriviaList()
                                              .EmptyIfWhitespace()
                                              .AddRange(lastStatement.GetTrailingTrivia());

            ExpressionStatementSyntax newExpressionStatement = expressionStatement
                                                               .ReplaceNode(invocationExpression, newInvocationExpression)
                                                               .WithLeadingTrivia(expressionStatement.GetLeadingTrivia())
                                                               .WithTrailingTrivia(trailingTrivia)
                                                               .WithFormatterAndSimplifierAnnotation();

            newStatements = newStatements.ReplaceAt(index, newExpressionStatement);

            return(await document.ReplaceStatementsAsync(statementsInfo, newStatements, cancellationToken).ConfigureAwait(false));
        }
示例#19
0
 internal static TextSpan LeadingTriviaSpan(this SyntaxToken token)
 {
     return(TextSpan.FromBounds(token.FullSpan.Start, token.Span.Start));
 }
示例#20
0
        public static void Analyze(
            SyntaxNodeAnalysisContext context,
            MemberInvocationExpressionInfo invocationInfo)
        {
            InvocationExpressionSyntax invocationExpression = invocationInfo.InvocationExpression;

            TextSpan span = TextSpan.FromBounds(invocationInfo.Name.Span.Start, invocationExpression.Span.End);

            if (invocationExpression.ContainsDirectives(span))
            {
                return;
            }

            SemanticModel     semanticModel     = context.SemanticModel;
            CancellationToken cancellationToken = context.CancellationToken;

            var methodSymbol = semanticModel.GetSymbol(invocationExpression, cancellationToken) as IMethodSymbol;

            if (methodSymbol == null)
            {
                return;
            }

            if (!ExtensionMethodInfo.TryCreate(methodSymbol, semanticModel, out ExtensionMethodInfo extensionMethodInfo))
            {
                return;
            }

            if (!extensionMethodInfo.MethodInfo.IsLinqSelect(allowImmutableArrayExtension: true))
            {
                return;
            }

            ITypeSymbol typeArgument = extensionMethodInfo.ReducedSymbolOrSymbol.TypeArguments[0];

            if (!typeArgument.IsReferenceType)
            {
                return;
            }

            if (typeArgument.SpecialType == SpecialType.System_Object)
            {
                return;
            }

            ExpressionSyntax expression = invocationExpression.ArgumentList?.Arguments.Last().Expression;

            SingleParameterLambdaExpressionInfo lambdaInfo = SyntaxInfo.SingleParameterLambdaExpressionInfo(expression);

            if (!lambdaInfo.Success)
            {
                return;
            }

            CastExpressionSyntax castExpression = GetCastExpression(lambdaInfo.Body);

            if (castExpression == null)
            {
                return;
            }

            if (!(castExpression.Expression is IdentifierNameSyntax identifierName))
            {
                return;
            }

            if (!string.Equals(lambdaInfo.Parameter.Identifier.ValueText, identifierName.Identifier.ValueText, StringComparison.Ordinal))
            {
                return;
            }

            var castSymbol = semanticModel.GetSymbol(castExpression, cancellationToken) as IMethodSymbol;

            if (castSymbol?.MethodKind == MethodKind.Conversion)
            {
                return;
            }

            context.ReportDiagnostic(
                DiagnosticDescriptors.CallCastInsteadOfSelect,
                Location.Create(invocationExpression.SyntaxTree, span));
        }
示例#21
0
 internal static TextSpan LeadingTriviaSpan(this SyntaxTrivia trivia)
 {
     return(TextSpan.FromBounds(trivia.FullSpan.Start, trivia.Span.Start));
 }
示例#22
0
        private void AddSwitchIndentationOperation(List <IndentBlockOperation> list, SyntaxNode node, OptionSet optionSet)
        {
            var section = node as SwitchSectionSyntax;

            if (section == null)
            {
                return;
            }

            // can this ever happen?
            if (section.Labels.Count == 0 &&
                section.Statements.Count == 0)
            {
                return;
            }

            var indentSwitchCase          = optionSet.GetOption(CSharpFormattingOptions.IndentSwitchCaseSection);
            var indentSwitchCaseWhenBlock = optionSet.GetOption(CSharpFormattingOptions.IndentSwitchCaseSectionWhenBlock);

            if (!indentSwitchCase && !indentSwitchCaseWhenBlock)
            {
                // Never indent
                return;
            }

            var alwaysIndent = indentSwitchCase && indentSwitchCaseWhenBlock;

            if (!alwaysIndent)
            {
                // Only one of these values can be true at this point.
                Debug.Assert(indentSwitchCase != indentSwitchCaseWhenBlock);

                var firstStatementIsBlock =
                    section.Statements.Count > 0 &&
                    section.Statements[0].IsKind(SyntaxKind.Block);

                if (indentSwitchCaseWhenBlock != firstStatementIsBlock)
                {
                    return;
                }
            }

            // see whether we are the last statement
            var switchStatement = node.Parent as SwitchStatementSyntax;
            var lastSection     = switchStatement.Sections.Last() == node;

            if (section.Statements.Count == 0)
            {
                // even if there is no statement under section, we still want indent operation
                var lastTokenOfLabel = section.Labels.Last().GetLastToken(includeZeroWidth: true);
                var nextToken        = lastTokenOfLabel.GetNextToken(includeZeroWidth: true);

                AddIndentBlockOperation(list, lastTokenOfLabel, lastTokenOfLabel,
                                        lastSection ?
                                        TextSpan.FromBounds(lastTokenOfLabel.FullSpan.End, nextToken.SpanStart) : TextSpan.FromBounds(lastTokenOfLabel.FullSpan.End, lastTokenOfLabel.FullSpan.End));
                return;
            }

            var startToken = section.Statements.First().GetFirstToken(includeZeroWidth: true);
            var endToken   = section.Statements.Last().GetLastToken(includeZeroWidth: true);

            // see whether we are the last statement
            var span = CommonFormattingHelpers.GetSpanIncludingTrailingAndLeadingTriviaOfAdjacentTokens(startToken, endToken);

            span = lastSection ? span : TextSpan.FromBounds(span.Start, endToken.FullSpan.End);

            AddIndentBlockOperation(list, startToken, endToken, span);
        }
        private bool TryGetSubTextChange(
            SourceText originalText, TextSpan visibleSpanInOriginalText,
            string rightText, TextSpan spanInOriginalText, TextSpan spanInRightText, out TextChange textChange)
        {
            textChange = default(TextChange);

            var visibleFirstLineInOriginalText = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.Start);
            var visibleLastLineInOriginalText  = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.End);

            // skip easy case
            // 1. things are out of visible span
            if (!visibleSpanInOriginalText.IntersectsWith(spanInOriginalText))
            {
                return(false);
            }

            // 2. there are no intersects
            var snippetInRightText = rightText.Substring(spanInRightText.Start, spanInRightText.Length);

            if (visibleSpanInOriginalText.Contains(spanInOriginalText) && visibleSpanInOriginalText.End != spanInOriginalText.End)
            {
                textChange = new TextChange(spanInOriginalText, snippetInRightText);
                return(true);
            }

            // okay, more complex case. things are intersecting boundaries.
            var firstLineOfRightTextSnippet = snippetInRightText.GetFirstLineText();
            var lastLineOfRightTextSnippet  = snippetInRightText.GetLastLineText();

            // there are 4 complex cases - these are all heuristic. not sure what better way I have. and the heuristic is heavily based on
            // text differ's behavior.

            // 1. it is a single line
            if (visibleFirstLineInOriginalText.LineNumber == visibleLastLineInOriginalText.LineNumber)
            {
                // don't do anything
                return(false);
            }

            // 2. replacement contains visible spans
            if (spanInOriginalText.Contains(visibleSpanInOriginalText))
            {
                // header
                // don't do anything

                // body
                textChange = new TextChange(
                    TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, visibleLastLineInOriginalText.Start),
                    snippetInRightText.Substring(firstLineOfRightTextSnippet.Length, snippetInRightText.Length - firstLineOfRightTextSnippet.Length - lastLineOfRightTextSnippet.Length));

                // footer
                // don't do anything

                return(true);
            }

            // 3. replacement intersects with start
            if (spanInOriginalText.Start < visibleSpanInOriginalText.Start &&
                visibleSpanInOriginalText.Start <= spanInOriginalText.End &&
                spanInOriginalText.End < visibleSpanInOriginalText.End)
            {
                // header
                // don't do anything

                // body
                if (visibleFirstLineInOriginalText.EndIncludingLineBreak <= spanInOriginalText.End)
                {
                    textChange = new TextChange(
                        TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, spanInOriginalText.End),
                        snippetInRightText.Substring(firstLineOfRightTextSnippet.Length));
                    return(true);
                }

                return(false);
            }

            // 4. replacement intersects with end
            if (visibleSpanInOriginalText.Start < spanInOriginalText.Start &&
                spanInOriginalText.Start <= visibleSpanInOriginalText.End &&
                visibleSpanInOriginalText.End <= spanInOriginalText.End)
            {
                // body
                if (spanInOriginalText.Start <= visibleLastLineInOriginalText.Start)
                {
                    textChange = new TextChange(
                        TextSpan.FromBounds(spanInOriginalText.Start, visibleLastLineInOriginalText.Start),
                        snippetInRightText.Substring(0, snippetInRightText.Length - lastLineOfRightTextSnippet.Length));
                    return(true);
                }

                // footer
                // don't do anything

                return(false);
            }

            // if it got hit, then it means there is a missing case
            throw ExceptionUtilities.Unreachable;
        }
示例#24
0
        public static bool TryParseGenericName(this SyntaxToken genericIdentifier, CancellationToken cancellationToken, out GenericNameSyntax genericName)
        {
            if (genericIdentifier.GetNextToken(includeSkipped: true).Kind() == SyntaxKind.LessThanToken)
            {
                var lastToken = genericIdentifier.FindLastTokenOfPartialGenericName();

                var syntaxTree = genericIdentifier.SyntaxTree;
                var name       = SyntaxFactory.ParseName(syntaxTree.GetText(cancellationToken).ToString(TextSpan.FromBounds(genericIdentifier.SpanStart, lastToken.Span.End)));

                genericName = name as GenericNameSyntax;
                return(genericName != null);
            }

            genericName = null;
            return(false);
        }
        private static async Task <Document> RefactorAsync(
            Document document,
            StatementSyntax statement,
            CancellationToken cancellationToken)
        {
            SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(statement);

            SyntaxList <StatementSyntax> statements = statementsInfo.Statements;

            int index = statements.IndexOf(statement);

            switch (statement)
            {
            case IfStatementSyntax ifStatement:
            {
                var expressionStatement = (ExpressionStatementSyntax)ifStatement.SingleNonBlockStatementOrDefault();

                var assignment = (AssignmentExpressionSyntax)expressionStatement.Expression;

                ExpressionSyntax left  = assignment.Left;
                ExpressionSyntax right = assignment.Right;

                BinaryExpressionSyntax coalesceExpression = CreateCoalesceExpression(
                    left.WithoutLeadingTrivia().WithTrailingTrivia(Space),
                    right.WithLeadingTrivia(Space),
                    semanticModel.GetTypeSymbol(left, cancellationToken));

                AssignmentExpressionSyntax newAssignment = assignment.WithRight(coalesceExpression.WithTriviaFrom(right));

                ExpressionStatementSyntax newNode = expressionStatement.WithExpression(newAssignment);

                IEnumerable <SyntaxTrivia> trivia = ifStatement.DescendantTrivia(TextSpan.FromBounds(ifStatement.SpanStart, expressionStatement.SpanStart));

                if (trivia.All(f => f.IsWhitespaceOrEndOfLineTrivia()))
                {
                    newNode = newNode.WithLeadingTrivia(ifStatement.GetLeadingTrivia());
                }
                else
                {
                    newNode = newNode
                              .WithLeadingTrivia(ifStatement.GetLeadingTrivia().Concat(trivia))
                              .WithFormatterAnnotation();
                }

                return(await document.ReplaceNodeAsync(ifStatement, newNode, cancellationToken).ConfigureAwait(false));
            }

            case ExpressionStatementSyntax expressionStatement:
            {
                var assignment = (AssignmentExpressionSyntax)expressionStatement.Expression;

                return(await RefactorAsync(document, expressionStatement, (IfStatementSyntax)statements[index + 1], index, statementsInfo, assignment.Right, semanticModel, cancellationToken).ConfigureAwait(false));
            }

            case LocalDeclarationStatementSyntax localDeclaration:
            {
                ExpressionSyntax value = localDeclaration
                                         .Declaration
                                         .Variables[0]
                                         .Initializer
                                         .Value;

                return(await RefactorAsync(document, localDeclaration, (IfStatementSyntax)statements[index + 1], index, statementsInfo, value, semanticModel, cancellationToken).ConfigureAwait(false));
            }

            default:
            {
                Debug.Fail(statement.Kind().ToString());

                return(document);
            }
            }
        }
 internal static TextSpan GetTextSpan(SyntaxNode expression, SyntaxToken openBracket)
 {
     Contract.ThrowIfFalse(openBracket.Parent is ArrayRankSpecifierSyntax && openBracket.Parent.Parent is ArrayTypeSyntax);
     return(TextSpan.FromBounds(expression.SpanStart, openBracket.Parent.Span.End));
 }
示例#27
0
        public void ToDiagnostic()
        {
            var tree       = SyntaxFactory.ParseCompilationUnit("class C { }").SyntaxTree;
            var syntaxNode = tree.GetRoot();

            // most rude edits have a single argument, list those that have different count:

            var arg0 = new HashSet <RudeEditKind>()
            {
                RudeEditKind.ActiveStatementUpdate,
                RudeEditKind.PartiallyExecutedActiveStatementUpdate,
                RudeEditKind.UpdateExceptionHandlerOfActiveTry,
                RudeEditKind.UpdateTryOrCatchWithActiveFinally,
                RudeEditKind.UpdateCatchHandlerAroundActiveStatement,
                RudeEditKind.FieldKindUpdate,
                RudeEditKind.TypeKindUpdate,
                RudeEditKind.AccessorKindUpdate,
                RudeEditKind.MethodKindUpdate,
                RudeEditKind.DeclareLibraryUpdate,
                RudeEditKind.DeclareAliasUpdate,
                RudeEditKind.InsertDllImport,
                RudeEditKind.MethodBodyAdd,
                RudeEditKind.MethodBodyDelete,
                RudeEditKind.GenericMethodUpdate,
                RudeEditKind.GenericTypeUpdate,
                RudeEditKind.ExperimentalFeaturesEnabled,
                RudeEditKind.AwaitStatementUpdate,
                RudeEditKind.InsertFile,
                RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas,
                RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement,
                RudeEditKind.SwitchBetweenLambdaAndLocalFunction,
                RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier,
                RudeEditKind.AddRecordPositionalParameter,
                RudeEditKind.DeleteRecordPositionalParameter,
                RudeEditKind.NotSupportedByRuntime,
                RudeEditKind.MakeMethodAsync,
                RudeEditKind.MakeMethodIterator,
                RudeEditKind.ChangeImplicitMainReturnType
            };

            var arg2 = new HashSet <RudeEditKind>()
            {
                RudeEditKind.ConstraintKindUpdate,
                RudeEditKind.InsertIntoStruct,
                RudeEditKind.ConstraintKindUpdate,
                RudeEditKind.InsertIntoStruct,
                RudeEditKind.ChangingCapturedVariableType,
                RudeEditKind.AccessingCapturedVariableInLambda,
                RudeEditKind.NotAccessingCapturedVariableInLambda,
                RudeEditKind.RenamingCapturedVariable,
                RudeEditKind.ChangingStateMachineShape,
                RudeEditKind.InternalError,
                RudeEditKind.MemberBodyInternalError,
            };

            var arg3 = new HashSet <RudeEditKind>()
            {
                RudeEditKind.InsertLambdaWithMultiScopeCapture,
                RudeEditKind.DeleteLambdaWithMultiScopeCapture,
            };

            var allKinds = Enum.GetValues(typeof(RudeEditKind)).Cast <RudeEditKind>();

            foreach (var kind in allKinds)
            {
                if (kind == RudeEditKind.None)
                {
                    continue;
                }

                if (arg0.Contains(kind))
                {
                    var re = new RudeEditDiagnostic(kind, TextSpan.FromBounds(1, 2));
                    var d  = re.ToDiagnostic(tree);
                    Assert.False(d.GetMessage().Contains("{"), kind.ToString());
                }
                else if (arg2.Contains(kind))
                {
                    var re = new RudeEditDiagnostic(kind, TextSpan.FromBounds(1, 2), syntaxNode, new[] { "<1>", "<2>" });
                    var d  = re.ToDiagnostic(tree);
                    Assert.True(d.GetMessage().Contains("<1>"), kind.ToString());
                    Assert.True(d.GetMessage().Contains("<2>"), kind.ToString());
                    Assert.False(d.GetMessage().Contains("{"), kind.ToString());
                }
                else if (arg3.Contains(kind))
                {
                    var re = new RudeEditDiagnostic(kind, TextSpan.FromBounds(1, 2), syntaxNode, new[] { "<1>", "<2>", "<3>" });
                    var d  = re.ToDiagnostic(tree);
                    Assert.True(d.GetMessage().Contains("<1>"), kind.ToString());
                    Assert.True(d.GetMessage().Contains("<2>"), kind.ToString());
                    Assert.True(d.GetMessage().Contains("<3>"), kind.ToString());
                    Assert.False(d.GetMessage().Contains("{"), kind.ToString());
                }
                else
                {
                    var re = new RudeEditDiagnostic(kind, TextSpan.FromBounds(1, 2), syntaxNode, new[] { "<1>" });
                    var d  = re.ToDiagnostic(tree);
                    Assert.True(d.GetMessage().Contains("<1>"), kind.ToString());
                    Assert.False(d.GetMessage().Contains("{"), kind.ToString());
                }
            }

            // check that all values are unique:
            AssertEx.Equal(allKinds, allKinds.Distinct());
        }
            private void ProcessFirstLine(TextSpan fullSpan, TextLine startLine)
            {
                string firstMessage = text.ToString(TextSpan.FromBounds(fullSpan.Start, startLine.End));

                ReportTodoCommentFromSingleLine(firstMessage, fullSpan.Start);
            }
        public async Task Proxy(TestHost testHost)
        {
            var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(
                testHost
                );

            if (testHost == TestHost.InProcess)
            {
                localComposition = localComposition.AddParts(typeof(MockEncServiceFactory));
            }

            using var localWorkspace = new TestWorkspace(composition: localComposition);

            MockEditAndContinueWorkspaceService mockEncService;
            var clientProvider =
                (InProcRemoteHostClientProvider?)localWorkspace.Services.GetService <IRemoteHostClientProvider>();

            if (testHost == TestHost.InProcess)
            {
                Assert.Null(clientProvider);

                mockEncService =
                    (MockEditAndContinueWorkspaceService)localWorkspace.Services.GetRequiredService <IEditAndContinueWorkspaceService>();
            }
            else
            {
                Assert.NotNull(clientProvider);
                clientProvider !.AdditionalRemoteParts = new[] { typeof(MockEncServiceFactory) };

                var client = await InProcRemoteHostClient
                             .GetTestClientAsync(localWorkspace)
                             .ConfigureAwait(false);

                var remoteWorkspace = client.TestData.WorkspaceManager.GetWorkspace();
                mockEncService =
                    (MockEditAndContinueWorkspaceService)remoteWorkspace.Services.GetRequiredService <IEditAndContinueWorkspaceService>();
            }

            localWorkspace.ChangeSolution(
                localWorkspace.CurrentSolution
                .AddProject("proj", "proj", LanguageNames.CSharp)
                .AddMetadataReferences(
                    TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)
                    )
                .AddDocument(
                    "test.cs",
                    SourceText.From("class C { }", Encoding.UTF8),
                    filePath: "test.cs"
                    ).Project.Solution
                );

            var solution = localWorkspace.CurrentSolution;
            var project  = solution.Projects.Single();
            var document = project.Documents.Single();

            var mockDiagnosticService = new MockDiagnosticAnalyzerService();

            void VerifyReanalyzeInvocation(ImmutableArray <DocumentId> documentIds)
            {
                AssertEx.Equal(documentIds, mockDiagnosticService.DocumentsToReanalyze);
                mockDiagnosticService.DocumentsToReanalyze.Clear();
            }

            var diagnosticUpdateSource      = new EditAndContinueDiagnosticUpdateSource();
            var emitDiagnosticsUpdated      = new List <DiagnosticsUpdatedArgs>();
            var emitDiagnosticsClearedCount = 0;

            diagnosticUpdateSource.DiagnosticsUpdated += (
                object sender,
                DiagnosticsUpdatedArgs args
                ) => emitDiagnosticsUpdated.Add(args);
            diagnosticUpdateSource.DiagnosticsCleared += (object sender, EventArgs args) =>
                                                         emitDiagnosticsClearedCount++;

            var span1          = new LinePositionSpan(new LinePosition(1, 2), new LinePosition(1, 5));
            var moduleId1      = new Guid("{44444444-1111-1111-1111-111111111111}");
            var methodId1      = new ManagedMethodId(moduleId1, token: 0x06000003, version: 2);
            var instructionId1 = new ManagedInstructionId(methodId1, ilOffset: 10);

            var as1 = new ManagedActiveStatementDebugInfo(
                instructionId1,
                documentName: "test.cs",
                span1.ToSourceSpan(),
                flags: ActiveStatementFlags.IsLeafFrame | ActiveStatementFlags.PartiallyExecuted
                );

            var methodId2 = new ManagedModuleMethodId(token: 0x06000002, version: 1);

            var exceptionRegionUpdate1 = new ManagedExceptionRegionUpdate(
                methodId2,
                delta: 1,
                newSpan: new SourceSpan(1, 2, 1, 5)
                );

            var document1 = localWorkspace.CurrentSolution.Projects.Single().Documents.Single();

            var activeSpans1 = ImmutableArray.Create(
                TextSpan.FromBounds(1, 2),
                TextSpan.FromBounds(3, 4)
                );

            var solutionActiveStatementSpanProvider = new SolutionActiveStatementSpanProvider(
                (documentId, cancellationToken) =>
            {
                Assert.Equal(document1.Id, documentId);
                return(new(activeSpans1));
            }
                );

            var documentActiveStatementSpanProvider = new DocumentActiveStatementSpanProvider(
                cancellationToken => new(activeSpans1)
                );

            var proxy = new RemoteEditAndContinueServiceProxy(localWorkspace);

            // StartDebuggingSession

            IManagedEditAndContinueDebuggerService?remoteDebuggeeModuleMetadataProvider = null;

            var called = false;

            mockEncService.StartDebuggingSessionImpl = (
                solution,
                debuggerService,
                captureMatchingDocuments
                ) =>
            {
                Assert.Equal("proj", solution.Projects.Single().Name);
                remoteDebuggeeModuleMetadataProvider = debuggerService;
                called = true;
            };

            await proxy
            .StartDebuggingSessionAsync(
                localWorkspace.CurrentSolution,
                debuggerService : new MockManagedEditAndContinueDebuggerService()
            {
                IsEditAndContinueAvailable = _ =>
                                             new ManagedEditAndContinueAvailability(
                    ManagedEditAndContinueAvailabilityStatus.NotAllowedForModule,
                    "can't do enc"
                    ),
                GetActiveStatementsImpl = () => ImmutableArray.Create(as1)
            },
                captureMatchingDocuments : false,
                CancellationToken.None
                )
            .ConfigureAwait(false);

            Assert.True(called);

            // BreakStateEntered

            mockEncService.BreakStateEnteredImpl = (
                out ImmutableArray <DocumentId> documentsToReanalyze
                ) =>
            {
                documentsToReanalyze = ImmutableArray.Create(document.Id);
            };

            await proxy
            .BreakStateEnteredAsync(mockDiagnosticService, CancellationToken.None)
            .ConfigureAwait(false);

            VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id));

            var activeStatement = (
                await remoteDebuggeeModuleMetadataProvider !
                .GetActiveStatementsAsync(CancellationToken.None)
                .ConfigureAwait(false)
                ).Single();

            Assert.Equal(as1.ActiveInstruction, activeStatement.ActiveInstruction);
            Assert.Equal(as1.SourceSpan, activeStatement.SourceSpan);
            Assert.Equal(as1.Flags, activeStatement.Flags);

            var availability = await remoteDebuggeeModuleMetadataProvider !
                               .GetAvailabilityAsync(moduleId1, CancellationToken.None)
                               .ConfigureAwait(false);

            Assert.Equal(
                new ManagedEditAndContinueAvailability(
                    ManagedEditAndContinueAvailabilityStatus.NotAllowedForModule,
                    "can't do enc"
                    ),
                availability
                );

            // EndDebuggingSession

            mockEncService.EndDebuggingSessionImpl = (
                out ImmutableArray <DocumentId> documentsToReanalyze
                ) =>
            {
                documentsToReanalyze = ImmutableArray.Create(document.Id);
            };

            await proxy
            .EndDebuggingSessionAsync(
                diagnosticUpdateSource,
                mockDiagnosticService,
                CancellationToken.None
                )
            .ConfigureAwait(false);

            VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id));
            Assert.Equal(1, emitDiagnosticsClearedCount);
            emitDiagnosticsClearedCount = 0;
            Assert.Empty(emitDiagnosticsUpdated);

            // HasChanges

            mockEncService.HasChangesImpl = (
                solution,
                activeStatementSpanProvider,
                sourceFilePath
                ) =>
            {
                Assert.Equal("proj", solution.Projects.Single().Name);
                Assert.Equal("test.cs", sourceFilePath);
                AssertEx.Equal(
                    activeSpans1,
                    activeStatementSpanProvider(document1.Id, CancellationToken.None).Result
                    );
                return(true);
            };

            Assert.True(
                await proxy
                .HasChangesAsync(
                    localWorkspace.CurrentSolution,
                    solutionActiveStatementSpanProvider,
                    "test.cs",
                    CancellationToken.None
                    )
                .ConfigureAwait(false)
                );

            // EmitSolutionUpdate

            var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(
                EditAndContinueErrorCode.ErrorReadingFile
                );

            mockEncService.EmitSolutionUpdateImpl = (solution, activeStatementSpanProvider) =>
            {
                var project = solution.Projects.Single();
                Assert.Equal("proj", project.Name);
                AssertEx.Equal(
                    activeSpans1,
                    activeStatementSpanProvider(document1.Id, CancellationToken.None).Result
                    );

                var deltas = ImmutableArray.Create(
                    new ManagedModuleUpdate(
                        module: moduleId1,
                        ilDelta: ImmutableArray.Create <byte>(1, 2),
                        metadataDelta: ImmutableArray.Create <byte>(3, 4),
                        pdbDelta: ImmutableArray.Create <byte>(5, 6),
                        updatedMethods: ImmutableArray.Create(0x06000001),
                        sequencePoints: ImmutableArray.Create(
                            new SequencePointUpdates(
                                "file.cs",
                                ImmutableArray.Create(new SourceLineUpdate(1, 2))
                                )
                            ),
                        activeStatements: ImmutableArray.Create(
                            new ManagedActiveStatementUpdate(
                                instructionId1.Method.Method,
                                instructionId1.ILOffset,
                                span1.ToSourceSpan()
                                )
                            ),
                        exceptionRegions: ImmutableArray.Create(exceptionRegionUpdate1)
                        )
                    );

                var syntaxTree = project.Documents
                                 .Single()
                                 .GetSyntaxTreeSynchronously(CancellationToken.None) !;

                var documentDiagnostic = Diagnostic.Create(
                    diagnosticDescriptor1,
                    Location.Create(syntaxTree, TextSpan.FromBounds(1, 2)),
                    new[] { "doc", "some error" }
                    );
                var projectDiagnostic = Diagnostic.Create(
                    diagnosticDescriptor1,
                    Location.None,
                    new[] { "proj", "some error" }
                    );

                var updates     = new ManagedModuleUpdates(ManagedModuleUpdateStatus.Ready, deltas);
                var diagnostics = ImmutableArray.Create(
                    (project.Id, ImmutableArray.Create(documentDiagnostic, projectDiagnostic))
                    );
                var documentsWithRudeEdits = ImmutableArray.Create(
                    (document1.Id, ImmutableArray <RudeEditDiagnostic> .Empty)
                    );

                return(new(updates, diagnostics, documentsWithRudeEdits));
            };

            var(updates, _, _) = await proxy
                                 .EmitSolutionUpdateAsync(
                localWorkspace.CurrentSolution,
                solutionActiveStatementSpanProvider,
                mockDiagnosticService,
                diagnosticUpdateSource,
                CancellationToken.None
                )
                                 .ConfigureAwait(false);

            VerifyReanalyzeInvocation(ImmutableArray.Create(document1.Id));

            Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status);

            Assert.Equal(1, emitDiagnosticsClearedCount);
            emitDiagnosticsClearedCount = 0;

            AssertEx.Equal(
                new[]
            {
                $"[{project.Id}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "some error")}",
                $"[{project.Id}] Error ENC1001: {string.Format(FeaturesResources.ErrorReadingFile, "proj", "some error")}"
            },
                emitDiagnosticsUpdated.Select(
                    update =>
            {
                var d = update
                        .GetPushDiagnostics(
                    localWorkspace,
                    InternalDiagnosticsOptions.NormalDiagnosticMode
                    )
                        .Single();
                return($"[{d.ProjectId}] {d.Severity} {d.Id}:"
                       + (
                           d.DataLocation != null
                                    ? $" {d.DataLocation.OriginalFilePath}({d.DataLocation.OriginalStartLine}, {d.DataLocation.OriginalStartColumn}, {d.DataLocation.OriginalEndLine}, {d.DataLocation.OriginalEndColumn}):"
                                    : ""
                           )
                       + $" {d.Message}");
            }
                    )
                );

            var delta = updates.Updates.Single();

            Assert.Equal(moduleId1, delta.Module);
            AssertEx.Equal(new byte[] { 1, 2 }, delta.ILDelta);
            AssertEx.Equal(new byte[] { 3, 4 }, delta.MetadataDelta);
            AssertEx.Equal(new byte[] { 5, 6 }, delta.PdbDelta);
            AssertEx.Equal(new[] { 0x06000001 }, delta.UpdatedMethods);

            var lineEdit = delta.SequencePoints.Single();

            Assert.Equal("file.cs", lineEdit.FileName);
            AssertEx.Equal(new[] { new SourceLineUpdate(1, 2) }, lineEdit.LineUpdates);
            Assert.Equal(exceptionRegionUpdate1, delta.ExceptionRegions.Single());

            var activeStatements = delta.ActiveStatements.Single();

            Assert.Equal(instructionId1.Method.Method, activeStatements.Method);
            Assert.Equal(instructionId1.ILOffset, activeStatements.ILOffset);
            Assert.Equal(span1, activeStatements.NewSpan.ToLinePositionSpan());

            // CommitSolutionUpdate

            mockEncService.CommitSolutionUpdateImpl = (
                out ImmutableArray <DocumentId> documentsToReanalyze
                ) =>
            {
                documentsToReanalyze = ImmutableArray.Create(document.Id);
            };

            await proxy
            .CommitSolutionUpdateAsync(mockDiagnosticService, CancellationToken.None)
            .ConfigureAwait(false);

            VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id));

            // DiscardSolutionUpdate

            called = false;
            mockEncService.DiscardSolutionUpdateImpl = () => called = true;
            await proxy.DiscardSolutionUpdateAsync(CancellationToken.None).ConfigureAwait(false);

            Assert.True(called);

            // GetCurrentActiveStatementPosition

            mockEncService.GetCurrentActiveStatementPositionImpl = (
                solution,
                activeStatementSpanProvider,
                instructionId
                ) =>
            {
                Assert.Equal("proj", solution.Projects.Single().Name);
                Assert.Equal(instructionId1, instructionId);
                AssertEx.Equal(
                    activeSpans1,
                    activeStatementSpanProvider(document1.Id, CancellationToken.None).Result
                    );
                return(new LinePositionSpan(new LinePosition(1, 2), new LinePosition(1, 5)));
            };

            Assert.Equal(
                span1,
                await proxy
                .GetCurrentActiveStatementPositionAsync(
                    localWorkspace.CurrentSolution,
                    solutionActiveStatementSpanProvider,
                    instructionId1,
                    CancellationToken.None
                    )
                .ConfigureAwait(false)
                );

            // IsActiveStatementInExceptionRegion

            mockEncService.IsActiveStatementInExceptionRegionImpl = (solution, instructionId) =>
            {
                Assert.Equal(instructionId1, instructionId);
                return(true);
            };

            Assert.True(
                await proxy
                .IsActiveStatementInExceptionRegionAsync(
                    localWorkspace.CurrentSolution,
                    instructionId1,
                    CancellationToken.None
                    )
                .ConfigureAwait(false)
                );

            // GetBaseActiveStatementSpans

            mockEncService.GetBaseActiveStatementSpansImpl = (solution, documentIds) =>
            {
                AssertEx.Equal(new[] { document1.Id }, documentIds);
                return(ImmutableArray.Create(
                           ImmutableArray.Create(
                               (
                                   span1,
                                   ActiveStatementFlags.IsNonLeafFrame
                                   | ActiveStatementFlags.PartiallyExecuted
                               )
                               )
                           ));
            };

            var baseActiveSpans = await proxy
                                  .GetBaseActiveStatementSpansAsync(
                localWorkspace.CurrentSolution,
                ImmutableArray.Create(document1.Id),
                CancellationToken.None
                )
                                  .ConfigureAwait(false);

            Assert.Equal(
                (
                    span1,
                    ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted
                ),
                baseActiveSpans.Single().Single()
                );

            // GetDocumentActiveStatementSpans

            mockEncService.GetAdjustedActiveStatementSpansImpl = (
                document,
                activeStatementSpanProvider
                ) =>
            {
                Assert.Equal("test.cs", document.Name);
                AssertEx.Equal(
                    activeSpans1,
                    activeStatementSpanProvider(CancellationToken.None).Result
                    );
                return(ImmutableArray.Create(
                           (
                               span1,
                               ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted
                           )
                           ));
            };

            var documentActiveSpans = await proxy
                                      .GetAdjustedActiveStatementSpansAsync(
                document1,
                documentActiveStatementSpanProvider,
                CancellationToken.None
                )
                                      .ConfigureAwait(false);

            Assert.Equal(
                (
                    span1,
                    ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.PartiallyExecuted
                ),
                documentActiveSpans.Single()
                );

            // GetDocumentActiveStatementSpans (default array)

            mockEncService.GetAdjustedActiveStatementSpansImpl = (document, _) => default;

            documentActiveSpans = await proxy
                                  .GetAdjustedActiveStatementSpansAsync(
                document1,
                documentActiveStatementSpanProvider,
                CancellationToken.None
                )
                                  .ConfigureAwait(false);

            Assert.True(documentActiveSpans.IsDefault);

            // OnSourceFileUpdatedAsync

            called = false;
            mockEncService.OnSourceFileUpdatedImpl = updatedDocument =>
            {
                Assert.Equal(document.Id, updatedDocument.Id);
                called = true;
            };

            await proxy
            .OnSourceFileUpdatedAsync(document, CancellationToken.None)
            .ConfigureAwait(false);

            Assert.True(called);
        }
        private SyntaxToken LexDirectiveToken()
        {
            _kind           = SyntaxKind.BadToken;
            _contextualKind = SyntaxKind.BadToken;

            _diagnostics.Clear();
            _start = _charReader.Position;

            var trailingTrivia = new List <SyntaxNode>();

            var isEndOfLine = false;

            switch (_charReader.Current)
            {
            case '#':
                NextChar();
                if (_charReader.Current == '#')
                {
                    NextChar();
                    _kind = SyntaxKind.HashHashToken;
                }
                else
                {
                    _kind = SyntaxKind.HashToken;
                    _currentDirectiveKind = null;
                }
                break;

            case '\r':
            case '\n':
                _kind = SyntaxKind.EndOfDirectiveToken;
                _currentDirectiveKind = null;
                isEndOfLine           = true;
                break;

            case '\0':
                _kind = SyntaxKind.EndOfDirectiveToken;
                _currentDirectiveKind = null;
                break;

            case '<':
                if (_currentDirectiveKind != SyntaxKind.IncludeKeyword)
                {
                    goto default;
                }
                ReadBracketedString();
                break;

            default:
                ReadToken();
                if (_contextualKind.IsPreprocessorDirective())
                {
                    _currentDirectiveKind = _contextualKind;
                }
                break;
            }

            var end         = _charReader.Position;
            var kind        = _kind;
            var span        = TextSpan.FromBounds(_start, end);
            var text        = File.Text.GetSubText(span).ToString();
            var fileSpan    = new SourceFileSpan(File, span);
            var diagnostics = _diagnostics.ToImmutableArray();

            LexDirectiveTrailingTrivia(trailingTrivia, kind, isEndOfLine);

            var token = new SyntaxToken(kind, _contextualKind, false, MakeAbsolute(span), fileSpan, text, _value,
                                        ImmutableArray <SyntaxNode> .Empty, trailingTrivia.ToImmutableArray(),
                                        diagnostics, null, false);

            return(token);
        }