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))); }
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()); } }
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); }
internal static TextSpan TrailingTriviaSpan(this SyntaxTrivia trivia) { return(TextSpan.FromBounds(trivia.Span.End, trivia.FullSpan.End)); }
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))); }
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)); } }
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); }
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());
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); }
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)); }
internal static TextSpan LeadingTriviaSpan(this SyntaxToken token) { return(TextSpan.FromBounds(token.FullSpan.Start, token.Span.Start)); }
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)); }
internal static TextSpan LeadingTriviaSpan(this SyntaxTrivia trivia) { return(TextSpan.FromBounds(trivia.FullSpan.Start, trivia.Span.Start)); }
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; }
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)); }
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); }