private FixAllState( FixAllProvider fixAllProvider, Document document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) { Contract.ThrowIfNull(project); if (diagnosticIds == null) { throw new ArgumentNullException(nameof(diagnosticIds)); } if (diagnosticIds.Any(d => d == null)) { throw new ArgumentException(WorkspacesResources.Supplied_diagnostic_cannot_be_null, nameof(diagnosticIds)); } this.FixAllProvider = fixAllProvider; this.Document = document; this.Project = project; this.CodeFixProvider = codeFixProvider ?? throw new ArgumentNullException(nameof(codeFixProvider)); this.Scope = scope; this.CodeActionEquivalenceKey = codeActionEquivalenceKey; this.DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); this.DiagnosticProvider = fixAllDiagnosticProvider ?? throw new ArgumentNullException(nameof(fixAllDiagnosticProvider)); }
internal FixAllContext( Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionId, IEnumerable<string> diagnosticIds, Func<Document, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getDocumentDiagnosticsAsync, Func<Project, bool, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getProjectDiagnosticsAsync, CancellationToken cancellationToken) : this(null, project, codeFixProvider, scope, codeActionId, diagnosticIds, getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync, cancellationToken) { }
/// <summary> /// Creates a new <see cref="FixAllContext"/>. /// Use this overload when applying fix all to a diagnostic with a source location. /// </summary> /// <param name="document">Document within which fix all occurrences was triggered.</param> /// <param name="codeFixProvider">Underlying <see cref="CodeFixes.CodeFixProvider"/> which triggered this fix all.</param> /// <param name="scope"><see cref="FixAllScope"/> to fix all occurrences.</param> /// <param name="codeActionEquivalenceKey">The <see cref="CodeAction.EquivalenceKey"/> value expected of a <see cref="CodeAction"/> participating in this fix all.</param> /// <param name="diagnosticIds">Diagnostic Ids to fix.</param> /// <param name="fixAllDiagnosticProvider"> /// <see cref="DiagnosticProvider"/> to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>. /// </param> /// <param name="cancellationToken">Cancellation token for fix all computation.</param> public FixAllContext( Document document, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) : this(document, document.Project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) { if (document == null) { throw new ArgumentNullException(nameof(document)); } }
internal FixAllState( FixAllProvider fixAllProvider, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) : this(fixAllProvider, null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider) { if (project == null) { throw new ArgumentNullException(nameof(project)); } }
internal FixAllState( FixAllProvider fixAllProvider, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) : this(fixAllProvider, null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider) { if (project == null) { throw new ArgumentNullException(nameof(project)); } }
/// <summary> /// Creates a new <see cref="FixAllContext"/>. /// Use this overload when applying fix all to a diagnostic with a source location. /// </summary> /// <param name="document">Document within which fix all occurrences was triggered.</param> /// <param name="codeFixProvider">Underlying <see cref="CodeFixes.CodeFixProvider"/> which triggered this fix all.</param> /// <param name="scope"><see cref="FixAllScope"/> to fix all occurrences.</param> /// <param name="codeActionEquivalenceKey">The <see cref="CodeAction.EquivalenceKey"/> value expected of a <see cref="CodeAction"/> participating in this fix all.</param> /// <param name="diagnosticIds">Diagnostic Ids to fix.</param> /// <param name="fixAllDiagnosticProvider"> /// <see cref="DiagnosticProvider"/> to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>. /// </param> /// <param name="cancellationToken">Cancellation token for fix all computation.</param> public FixAllContext( Document document, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) : this(document, document.Project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) { if (document == null) { throw new ArgumentNullException(nameof(document)); } }
public void FixAllOccurrencesTriggeredFromGeneratedCode(FixAllScope scope) { var markup = @"// <auto-generated/> using System; using $$System.Threading; class C { public IntPtr X1 { get; set; } }"; var secondFile = @" using System; using System.Threading; class D { public IntPtr X1 { get; set; } }"; var expectedSecondFile = @" using System; class D { public IntPtr X1 { get; set; } }"; VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), "D.cs", secondFile, open: false); // Switch to the main document we'll be editing VisualStudio.SolutionExplorer.OpenFile(new ProjectUtils.Project(ProjectName), "Class1.cs"); // Verify that applying a Fix All operation does not change generated file, but does change other files. // ⚠ This is a statement of the current behavior, and not a claim regarding correctness of the design. // The current behavior is observable; any change to this behavior should be part of an intentional design // change. MarkupTestFile.GetPosition(markup, out var expectedText, out int _); SetUpEditor(markup); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Remove Unnecessary Usings", applyFix: true, fixAllScope: scope); AssertEx.EqualOrDiff(expectedText, VisualStudio.Editor.GetText()); VisualStudio.SolutionExplorer.OpenFile(new ProjectUtils.Project(ProjectName), "D.cs"); AssertEx.EqualOrDiff(expectedSecondFile, VisualStudio.Editor.GetText()); }
private static FixAllContext GetFixAllContext( IEnumerable <Diagnostic> diagnostics, DiagnosticAnalyzer provider, CodeFixProvider fixer, TestDiagnosticAnalyzerDriver testDriver, Document document, FixAllScope scope, string fixAllActionId) { Assert.NotEmpty(diagnostics); if (scope == FixAllScope.Custom) { // Bulk fixing diagnostics in selected scope. var diagnosticsToFix = ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable(KeyValuePair.Create(document, diagnostics.ToImmutableArray()))); return(FixMultipleContext.Create(diagnosticsToFix, fixer, fixAllActionId, CancellationToken.None)); } var diagnostic = diagnostics.First(); Func <Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getDocumentDiagnosticsAsync = async(d, diagIds, c) => { var root = await d.GetSyntaxRootAsync(); var diags = await testDriver.GetDocumentDiagnosticsAsync(provider, d, root.FullSpan); diags = diags.Where(diag => diagIds.Contains(diag.Id)); return(diags); }; Func <Project, bool, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getProjectDiagnosticsAsync = async(p, includeAllDocumentDiagnostics, diagIds, c) => { var diags = includeAllDocumentDiagnostics ? await testDriver.GetAllDiagnosticsAsync(provider, p) : await testDriver.GetProjectDiagnosticsAsync(provider, p); diags = diags.Where(diag => diagIds.Contains(diag.Id)); return(diags); }; var diagnosticIds = ImmutableHashSet.Create(diagnostic.Id); var fixAllDiagnosticProvider = new FixAllCodeActionContext.FixAllDiagnosticProvider(diagnosticIds, getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync); return(diagnostic.Location.IsInSource ? new FixAllContext(document, fixer, scope, fixAllActionId, diagnosticIds, fixAllDiagnosticProvider, CancellationToken.None) : new FixAllContext(document.Project, fixer, scope, fixAllActionId, diagnosticIds, fixAllDiagnosticProvider, CancellationToken.None)); }
/// <summary> /// Transforms this context into the public <see cref="FixAllContext"/> to be used for <see cref="FixAllProvider.GetFixAsync(FixAllContext)"/> invocation. /// </summary> internal FixAllContext GetContextForScopeAndActionId(FixAllScope scope, string codeActionEquivalenceKey) { if (this.Scope == scope && this.CodeActionEquivalenceKey == codeActionEquivalenceKey) { return(this); } if (this.Document != null) { return(new FixAllContext(this.Document, this.CodeFixProvider, scope, codeActionEquivalenceKey, this.DiagnosticIds, _getDocumentDiagnosticsAsync, _getProjectDiagnosticsAsync, this.CancellationToken)); } return(new FixAllContext(this.Project, this.CodeFixProvider, scope, codeActionEquivalenceKey, this.DiagnosticIds, _getDocumentDiagnosticsAsync, _getProjectDiagnosticsAsync, this.CancellationToken)); }
/// <summary> /// Creates a new <see cref="FixAllContext"/>. /// Use this overload when applying fix all to a diagnostic with no source location, i.e. <see cref="Location.None"/>. /// </summary> /// <param name="project">Project within which fix all occurrences was triggered.</param> /// <param name="codeFixProvider">Underlying <see cref="CodeFixes.CodeFixProvider"/> which triggered this fix all.</param> /// <param name="scope"><see cref="FixAllScope"/> to fix all occurrences.</param> /// <param name="codeActionEquivalenceKey">The <see cref="CodeAction.EquivalenceKey"/> value expected of a <see cref="CodeAction"/> participating in this fix all.</param> /// <param name="diagnosticIds">Diagnostic Ids to fix.</param> /// <param name="fixAllDiagnosticProvider"> /// <see cref="DiagnosticProvider"/> to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>. /// </param> /// <param name="cancellationToken">Cancellation token for fix all computation.</param> public FixAllContext( Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) : this(new FixAllState(null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider), new ProgressTracker(), cancellationToken) { if (project == null) { throw new ArgumentNullException(nameof(project)); } }
public static string GetDefaultFixAllTitle( FixAllScope fixAllScope, ImmutableHashSet <string> diagnosticIds, Document?triggerDocument, Project triggerProject) { var diagnosticId = diagnosticIds.First(); return(fixAllScope switch { FixAllScope.Custom => string.Format(WorkspaceExtensionsResources.Fix_all_0, diagnosticId), FixAllScope.Document => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, diagnosticId, triggerDocument !.Name), FixAllScope.Project => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_1, diagnosticId, triggerProject.Name), FixAllScope.Solution => string.Format(WorkspaceExtensionsResources.Fix_all_0_in_Solution, diagnosticId), _ => throw ExceptionUtilities.UnexpectedValue(fixAllScope), });
/// <summary> /// Transforms this context into the public <see cref="FixAllContext"/> to be used for <see cref="FixAllProvider.GetFixAsync(FixAllContext)"/> invocation. /// </summary> internal FixAllContext GetContextForScopeAndActionId(FixAllScope scope, string codeActionEquivalenceKey) { if (this.Scope == scope && this.CodeActionEquivalenceKey == codeActionEquivalenceKey) { return this; } if (this.Document != null) { return new FixAllContext(this.Document, this.CodeFixProvider, scope, codeActionEquivalenceKey, this.DiagnosticIds, _getDocumentDiagnosticsAsync, _getProjectDiagnosticsAsync, this.CancellationToken); } return new FixAllContext(this.Project, this.CodeFixProvider, scope, codeActionEquivalenceKey, this.DiagnosticIds, _getDocumentDiagnosticsAsync, _getProjectDiagnosticsAsync, this.CancellationToken); }
public async Task <ImmutableDictionary <Document, ImmutableArray <TextSpan> > > GetFixAllSpansAsync( Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken) { Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); var decl = await GetContainingMemberOrTypeDeclarationAsync(document, fixAllScope, diagnosticSpan, cancellationToken).ConfigureAwait(false); if (decl == null) { return(await GetFixAllSpansIfWithinGlobalStatementAsync(document, diagnosticSpan, fixAllScope, cancellationToken).ConfigureAwait(false)); } if (fixAllScope == FixAllScope.ContainingMember) { return(ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable( KeyValuePairUtil.Create(document, ImmutableArray.Create(decl.FullSpan))))); } else { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetDeclaredSymbol(decl, cancellationToken); if (symbol?.DeclaringSyntaxReferences.Length > 1) { var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var builder = PooledDictionary <Document, ArrayBuilder <TextSpan> > .GetInstance(); foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var documentForLocation = document.Project.GetDocument(syntaxRef.SyntaxTree); Contract.ThrowIfNull(documentForLocation); var root = await syntaxRef.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var partialDeclSpan = syntaxFacts.GetContainingTypeDeclaration(root, syntaxRef.Span.Start) !.FullSpan; builder.MultiAdd(documentForLocation, partialDeclSpan); } return(builder.ToImmutableMultiDictionaryAndFree()); } else { return(ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable( KeyValuePairUtil.Create(document, ImmutableArray.Create(decl.FullSpan))))); } } }
private void VerifyFixAll( string language, DiagnosticAnalyzer analyzerOpt, CodeFixProvider codeFixProvider, string[] oldSources, string[] newSources, FixAllScope fixAllScope, int?codeFixIndex, bool allowNewCompilerDiagnostics, bool allowUnsafeCode, TestValidationMode validationMode) { var runner = new CodeFixRunner(analyzerOpt, codeFixProvider, validationMode); Assert.True(oldSources.Length == newSources.Length, "Length of expected and actual sources arrays must match."); Document[] documents = CreateDocuments(oldSources, language, allowUnsafeCode: allowUnsafeCode); VerifyFixAll(runner, documents, newSources, fixAllScope, codeFixIndex, allowNewCompilerDiagnostics); }
private FixAllContext( Document document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionId, IEnumerable <string> diagnosticIds, Func <Project, Document, ImmutableHashSet <string>, CancellationToken, Task <IEnumerable <Diagnostic> > > getDiagnosticsAsync, CancellationToken cancellationToken) { this.Document = document; this.Project = project; this.CodeFixProvider = codeFixProvider; this.Scope = scope; this.CodeActionId = codeActionId; this.DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); this.getDiagnosticsAsync = getDiagnosticsAsync; this.CancellationToken = cancellationToken; }
/// <summary> /// Creates a new <see cref="FixAllContext"/>. /// Use this overload when applying fix all to a diagnostic with a source location. /// <para> /// This overload cannot be used with <see cref="FixAllScope.ContainingMember"/> or /// <see cref="FixAllScope.ContainingType"/> value for the <paramref name="scope"/>. /// For those fix all scopes, use the <see cref="FixAllContext"/> constructor that /// takes a 'diagnosticSpan' parameter to identify the containing member or type based /// on this span. /// </para> /// </summary> /// <param name="document">Document within which fix all occurrences was triggered.</param> /// <param name="codeFixProvider">Underlying <see cref="CodeFixes.CodeFixProvider"/> which triggered this fix all.</param> /// <param name="scope"><see cref="FixAllScope"/> to fix all occurrences.</param> /// <param name="codeActionEquivalenceKey">The <see cref="CodeAction.EquivalenceKey"/> value expected of a <see cref="CodeAction"/> participating in this fix all.</param> /// <param name="diagnosticIds">Diagnostic Ids to fix.</param> /// <param name="fixAllDiagnosticProvider"> /// <see cref="DiagnosticProvider"/> to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>. /// </param> /// <param name="cancellationToken">Cancellation token for fix all computation.</param> public FixAllContext( Document document, CodeFixProvider codeFixProvider, FixAllScope scope, string?codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) #pragma warning disable RS0030 // Do not used banned APIs - It is fine to invoke the public FixAllContext constructor here. : this(document, diagnosticSpan : null, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) #pragma warning restore RS0030 // Do not used banned APIs { if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) { throw new ArgumentException(WorkspacesResources.FixAllScope_ContainingType_and_FixAllScope_ContainingMember_are_not_supported_with_this_constructor, nameof(scope)); } }
/// <summary> /// Fix the solution by applying the code fix. /// </summary> /// <returns>The fixed solution or the same instance if no fix.</returns> internal static async Task <Solution> ApplyAsync(CodeFixProvider codeFix, FixAllScope scope, TestDiagnosticProvider diagnosticProvider, CancellationToken cancellationToken) { var context = new FixAllContext( diagnosticProvider.Document, codeFix, scope, diagnosticProvider.EquivalenceKey, codeFix.FixableDiagnosticIds, diagnosticProvider, cancellationToken); var action = await codeFix.GetFixAllProvider().GetFixAsync(context).ConfigureAwait(false); var operations = await action.GetOperationsAsync(cancellationToken) .ConfigureAwait(false); return(operations.OfType <ApplyChangesOperation>() .Single() .ChangedSolution); }
private static async Task <SyntaxNode?> GetContainingMemberOrTypeDeclarationAsync( Document document, FixAllScope fixAllScope, TextSpan span, CancellationToken cancellationToken) { Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var startContainer = fixAllScope == FixAllScope.ContainingMember ? syntaxFacts.GetContainingMemberDeclaration(root, span.Start) : syntaxFacts.GetContainingTypeDeclaration(root, span.Start); if (startContainer == null) { return(null); } if (fixAllScope == FixAllScope.ContainingMember && !syntaxFacts.IsMethodLevelMember(startContainer)) { return(null); } if (span.IsEmpty) { return(startContainer); } var endContainer = fixAllScope == FixAllScope.ContainingMember ? syntaxFacts.GetContainingMemberDeclaration(root, span.End) : syntaxFacts.GetContainingTypeDeclaration(root, span.End); if (startContainer == endContainer) { return(startContainer); } return(null); }
public async Task WhenGetActionIsScoped_ThenReturnOnlyItemsFromCorrectScope(FixAllScope scope) { using (var host = GetHost(true)) { var inScopeFile = CreateTestProjectWithDocument(host, @" internal class InvalidFormatIDE0055ExpectedHere{} "); var notInScopeFile = CreateTestProjectWithDocument(host, @" class NonInternalIDEIDE0040 { } "); var resultFromDocument = await GetFixAllTargets(host, inScopeFile, scope); Assert.Contains(resultFromDocument.Items, x => x.Id == "IDE0055"); Assert.DoesNotContain(resultFromDocument.Items, x => x.Id == "IDE0040"); } }
internal FixAllState( FixAllProvider fixAllProvider, TextSpan?diagnosticSpan, Document?document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string?codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, CodeActionOptionsProvider codeActionOptionsProvider) : base(fixAllProvider, document, project, codeFixProvider, codeActionOptionsProvider, scope, codeActionEquivalenceKey) { // We need the trigger diagnostic span for span based fix all scopes, i.e. FixAllScope.ContainingMember and FixAllScope.ContainingType Debug.Assert(diagnosticSpan.HasValue || scope is not FixAllScope.ContainingMember or FixAllScope.ContainingType); DiagnosticSpan = diagnosticSpan; DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); DiagnosticProvider = fixAllDiagnosticProvider; }
private FixAllState( FixAllProvider fixAllProvider, Document document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) { Contract.ThrowIfNull(project); if (codeFixProvider == null) { throw new ArgumentNullException(nameof(codeFixProvider)); } if (diagnosticIds == null) { throw new ArgumentNullException(nameof(diagnosticIds)); } if (diagnosticIds.Any(d => d == null)) { throw new ArgumentException(WorkspacesResources.DiagnosticCannotBeNull, nameof(diagnosticIds)); } if (fixAllDiagnosticProvider == null) { throw new ArgumentNullException(nameof(fixAllDiagnosticProvider)); } this.FixAllProvider = fixAllProvider; this.Document = document; this.Project = project; this.CodeFixProvider = codeFixProvider; this.Scope = scope; this.CodeActionEquivalenceKey = codeActionEquivalenceKey; this.DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); this.DiagnosticProvider = fixAllDiagnosticProvider; }
/// <summary> /// Fix the solution by applying the code fix. /// </summary> /// <returns>The fixed solution or the same instance if no fix.</returns> internal static async Task <Solution> ApplyAsync(CodeFixProvider fix, FixAllScope scope, TestDiagnosticProvider diagnosticProvider, CancellationToken cancellationToken) { var context = new FixAllContext( diagnosticProvider.Document, fix, scope, diagnosticProvider.EquivalenceKey, fix.FixableDiagnosticIds, diagnosticProvider, cancellationToken); var action = await fix.GetFixAllProvider().GetFixAsync(context).ConfigureAwait(false); var operations = await action.GetOperationsAsync(cancellationToken) .ConfigureAwait(false); if (operations.TrySingleOfType(out ApplyChangesOperation? operation)) { return(operation !.ChangedSolution); } throw new InvalidOperationException($"Expected one operation, was {string.Join(", ", operations)}"); }
internal FixAllState( FixAllProvider?fixAllProvider, Document?document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string?codeActionEquivalenceKey, IEnumerable <string> diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, CodeActionOptionsProvider codeActionOptionsProvider) { Debug.Assert(document == null || document.Project == project); FixAllProvider = fixAllProvider; Document = document; Project = project; CodeFixProvider = codeFixProvider; Scope = scope; CodeActionEquivalenceKey = codeActionEquivalenceKey; DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); DiagnosticProvider = fixAllDiagnosticProvider; CodeActionOptionsProvider = codeActionOptionsProvider; }
private async Task TestFixAllExecuterAsync(int codepage, FixAllScope scope) { await new CSharpTest { TestSources = { SourceText.From("class Foo { }", Encoding.GetEncoding(codepage)), SourceText.From("class Bar { }", Encoding.GetEncoding(codepage)), }, ExpectedDiagnostics = { Diagnostic().WithLocation("Test0.cs", 1, 1), Diagnostic().WithLocation("Test1.cs", 1, 1), }, FixedSources = { SourceText.From("class Foo { }", Encoding.UTF8), SourceText.From("class Bar { }", Encoding.UTF8), }, NumberOfFixAllIterations = 1, NumberOfFixAllInDocumentIterations = 2, }.RunAsync(CancellationToken.None).ConfigureAwait(false); }
protected override async Task <ImmutableDictionary <Document, ImmutableArray <TextSpan> > > GetFixAllSpansIfWithinGlobalStatementAsync( Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken) { Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindNode(diagnosticSpan); if (node.GetAncestorOrThis <GlobalStatementSyntax>() is null) { return(ImmutableDictionary <Document, ImmutableArray <TextSpan> > .Empty); } // Compute the fix all span for the global statements to be fixed. // If the file has type or namespace declaration towards the end, they need to be excluded // from the fix all span. var fixAllSpan = root.FullSpan; var firstTypeOrNamespaceDecl = root.ChildNodes().FirstOrDefault(n => SyntaxFacts.IsNamespaceMemberDeclaration(n.Kind())); if (firstTypeOrNamespaceDecl is not null) { // Bail out for compiler error case where a type or namespace declaration precedes a global statement. // C# compiler requires all global statements to preceed type and namespace declarations. var globalStatements = root.ChildNodes().OfType <GlobalStatementSyntax>(); if (globalStatements.Any(g => firstTypeOrNamespaceDecl.SpanStart < g.SpanStart)) { return(ImmutableDictionary <Document, ImmutableArray <TextSpan> > .Empty); } fixAllSpan = new TextSpan(root.FullSpan.Start, firstTypeOrNamespaceDecl.FullSpan.Start - 1); } return(ImmutableDictionary <Document, ImmutableArray <TextSpan> > .Empty .Add(document, ImmutableArray.Create(fixAllSpan))); }
/// <summary> /// Verifies that /// 1. <paramref name="codeWithErrorsIndicated"/> produces the expected diagnostics /// 2. The code fix fixes the code. /// </summary> /// <param name="analyzer">The analyzer to run on the code..</param> /// <param name="codeFix">The code fix to apply.</param> /// <param name="codeWithErrorsIndicated">The code with error positions indicated.</param> /// <param name="fixedCode">The expected code produced by the code fix.</param> /// <param name="fixTitle">The title of the fix to apply if more than one.</param> /// <param name="suppressedDiagnostics">The diagnostics to suppress when compiling.</param> /// <param name="metadataReferences">The meta data metadataReferences to add to the compilation.</param> /// <param name="allowCompilationErrors">If compilation errors are accepted in the fixed code.</param> /// <param name="scope">The scope to apply fixes for.</param> public static void FixAllByScope(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, IReadOnlyList <string> codeWithErrorsIndicated, IReadOnlyList <string> fixedCode, string fixTitle, IEnumerable <string> suppressedDiagnostics, IEnumerable <MetadataReference> metadataReferences, AllowCompilationErrors allowCompilationErrors, FixAllScope scope) { var diagnosticsAndSources = DiagnosticsAndSources.CreateFromCodeWithErrorsIndicated(analyzer, codeWithErrorsIndicated); VerifyAnalyzerSupportsDiagnostics(analyzer, diagnosticsAndSources.ExpectedDiagnostics); VerifyCodeFixSupportsAnalyzer(analyzer, codeFix); var sln = CodeFactory.CreateSolution(diagnosticsAndSources, analyzer, suppressedDiagnostics, metadataReferences); var diagnostics = Analyze.GetDiagnostics(sln, analyzer); VerifyDiagnostics(diagnosticsAndSources, diagnostics); FixAllByScope(analyzer, codeFix, sln, fixedCode, fixTitle, allowCompilationErrors, scope); }
private static void FixAllByScope(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, Solution sln, IReadOnlyList <string> fixedCode, string fixTitle, AllowCompilationErrors allowCompilationErrors, FixAllScope scope) { VerifyCodeFixSupportsAnalyzer(analyzer, codeFix); var fixedSolution = Fix.ApplyAllFixableScopeByScopeAsync(sln, analyzer, codeFix, scope, fixTitle, CancellationToken.None).GetAwaiter().GetResult(); AreEqualAsync(fixedCode, fixedSolution, $"Applying fixes for {scope} failed.").GetAwaiter().GetResult(); if (allowCompilationErrors == AllowCompilationErrors.No) { VerifyNoCompilerErrorsAsync(codeFix, fixedSolution).GetAwaiter().GetResult(); } }
/// <summary> /// Creates a new <see cref="FixAllContext"/>. /// Use this overload when applying fix all to a diagnostic with no source location, i.e. <see cref="Location.None"/>. /// </summary> /// <param name="project">Project within which fix all occurrences was triggered.</param> /// <param name="codeFixProvider">Underlying <see cref="CodeFixes.CodeFixProvider"/> which triggered this fix all.</param> /// <param name="scope"><see cref="FixAllScope"/> to fix all occurrences.</param> /// <param name="codeActionEquivalenceKey">The <see cref="CodeAction.EquivalenceKey"/> value expected of a <see cref="CodeAction"/> participating in this fix all.</param> /// <param name="diagnosticIds">Diagnostic Ids to fix.</param> /// <param name="fixAllDiagnosticProvider"> /// <see cref="DiagnosticProvider"/> to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>. /// </param> /// <param name="cancellationToken">Cancellation token for fix all computation.</param> public FixAllContext( Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) : this(new FixAllState(null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider), new ProgressTracker(), cancellationToken) { if (project == null) { throw new ArgumentNullException(nameof(project)); } }
private async Task TestFixAllWithMultipleEncodingsAsync(FixAllScope scope) { string[] testCode = new[] { "class Foo { }", "class Bar { }" }; this.fileEncoding = Encoding.Unicode; // Create a project using the specified encoding Project project = this.CreateProject(testCode); project = project.AddDocument("Test2.cs", SourceText.From("class FooBar { }", Encoding.UTF7)).Project; Project oldProject = project; Workspace workspace = project.Solution.Workspace; var codeFixer = this.GetCSharpCodeFixProvider(); var fixAllProvider = codeFixer.GetFixAllProvider(); var diagnostics = new List<Diagnostic>(); var descriptor = this.GetCSharpDiagnosticAnalyzers().First().SupportedDiagnostics.First(); foreach (var document in project.Documents) { // Create a diagnostic for the document to fix var properties = ImmutableDictionary<string, string>.Empty.SetItem(SA1412StoreFilesAsUtf8.EncodingProperty, (await document.GetTextAsync().ConfigureAwait(false)).Encoding.WebName); var diagnostic = Diagnostic.Create( descriptor, Location.Create(await document.GetSyntaxTreeAsync().ConfigureAwait(false), TextSpan.FromBounds(0, 0)), properties); diagnostics.Add(diagnostic); } FixAllContext fixAllContext = new FixAllContext( project.Documents.First(), codeFixer, scope, nameof(SA1412CodeFixProvider) + "." + this.fileEncoding.WebName, new[] { SA1412StoreFilesAsUtf8.DiagnosticId }, TestDiagnosticProvider.Create(diagnostics.ToImmutableArray()), CancellationToken.None); CodeAction codeAction = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); var operation = codeAction.GetOperationsAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()[0]; operation.Apply(workspace, CancellationToken.None); // project should now have the "fixed document" in it. // Because of limitations in Roslyn the fixed document should // have a different DocumentId then the broken document project = workspace.CurrentSolution.Projects.First(); Assert.Equal(3, project.DocumentIds.Count); // The order of the document ids is now different from the order before. for (int i = 1; i < 3; i++) { DocumentId documentId = project.DocumentIds[i]; SourceText sourceText = await project.GetDocument(documentId).GetTextAsync().ConfigureAwait(false); Assert.Equal(testCode[i - 1], sourceText.ToString()); Assert.Equal(Encoding.UTF8, sourceText.Encoding); Assert.NotEqual(oldProject.DocumentIds[i - 1], project.DocumentIds[i]); } // Check that Test2.cs was not changed DocumentId otherDocumentId = project.DocumentIds[0]; var otherDocument = project.GetDocument(otherDocumentId); Assert.Equal("Test2.cs", otherDocument.Name); SourceText otherDocumentText = await otherDocument.GetTextAsync().ConfigureAwait(false); Assert.Equal("class FooBar { }", otherDocumentText.ToString()); Assert.Equal(Encoding.UTF7, otherDocumentText.Encoding); Assert.Equal(oldProject.DocumentIds[2], project.DocumentIds[0]); }
private static async Task<Document> GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray<DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int numberOfIterations, CancellationToken cancellationToken) { int expectedNumberOfIterations = numberOfIterations; if (numberOfIterations < 0) { numberOfIterations = -numberOfIterations; } var previousDiagnostics = ImmutableArray.Create<Diagnostic>(); var fixAllProvider = codeFixProvider.GetFixAllProvider(); if (fixAllProvider == null) { return null; } bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } if (--numberOfIterations < 0) { Assert.True(false, "The upper limit for the number of fix all iterations was exceeded"); } string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)) { // do not pass unsupported diagnostics to a code fix provider continue; } var actions = new List<CodeAction>(); var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (actions.Count > (codeFixIndex ?? 0)) { equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey; break; } } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); IEnumerable<string> analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id); IEnumerable<string> compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal)); IEnumerable<string> disabledDiagnosticIds = document.Project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key); IEnumerable<string> relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct(); FixAllContext fixAllContext = new FixAllContext(document, codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return document; } var fixedDocument = await ApplyFixAsync(document, action, cancellationToken).ConfigureAwait(false); if (fixedDocument != document) { done = false; var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document. document = document.WithText(newText); } } while (!done); if (expectedNumberOfIterations >= 0) { Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations"); } return document; }
private async Task<IEnumerable<Tuple<Diagnostic, CodeFixCollection>>> GetDiagnosticAndFixesAsync( IEnumerable<Diagnostic> diagnostics, DiagnosticAnalyzer provider, CodeFixProvider fixer, TestDiagnosticAnalyzerDriver testDriver, Document document, TextSpan span, FixAllScope? scope, string fixAllActionId) { Assert.NotEmpty(diagnostics); var result = new List<Tuple<Diagnostic, CodeFixCollection>>(); if (scope == null) { // Simple code fix. foreach (var diagnostic in diagnostics) { var fixes = new List<CodeFix>(); var context = new CodeFixContext(document, diagnostic, (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context); if (fixes.Any()) { var codeFix = new CodeFixCollection( fixer, diagnostic.Location.SourceSpan, fixes, fixAllState: null, supportedScopes: null, firstDiagnostic: null); result.Add(Tuple.Create(diagnostic, codeFix)); } } } else { // Fix all fix. var fixAllProvider = fixer.GetFixAllProvider(); Assert.NotNull(fixAllProvider); var fixAllState = GetFixAllState(fixAllProvider, diagnostics, provider, fixer, testDriver, document, scope.Value, fixAllActionId); var fixAllContext = fixAllState.CreateFixAllContext(new ProgressTracker(), CancellationToken.None); var fixAllFix = await fixAllProvider.GetFixAsync(fixAllContext); if (fixAllFix != null) { // Same fix applies to each diagnostic in scope. foreach (var diagnostic in diagnostics) { var diagnosticSpan = diagnostic.Location.IsInSource ? diagnostic.Location.SourceSpan : default(TextSpan); var codeFix = new CodeFixCollection( fixAllProvider, diagnosticSpan, ImmutableArray.Create(new CodeFix(document.Project, fixAllFix, diagnostic)), fixAllState: null, supportedScopes: null, firstDiagnostic: null); result.Add(Tuple.Create(diagnostic, codeFix)); } } } return result; }
public void ApplyLightBulbAction(string action, FixAllScope? fixAllScope) { _inProc.ApplyLightBulbAction(action, fixAllScope); }
public FixAllState WithScopeAndEquivalenceKey(FixAllScope scope, string codeActionEquivalenceKey) { if (this.Scope == scope && this.CodeActionEquivalenceKey == codeActionEquivalenceKey) { return this; } return new FixAllState( this.FixAllProvider, this.Document, this.Project, this.CodeFixProvider, scope, codeActionEquivalenceKey, this.DiagnosticIds, this.DiagnosticProvider); }
private FixAllContext( Document document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, Func<Document, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getDocumentDiagnosticsAsync, Func<Project, bool, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getProjectDiagnosticsAsync, CancellationToken cancellationToken) { this.Document = document; this.Project = project; this.CodeFixProvider = codeFixProvider; this.Scope = scope; this.CodeActionEquivalenceKey = codeActionEquivalenceKey; this.DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); _getDocumentDiagnosticsAsync = getDocumentDiagnosticsAsync; _getProjectDiagnosticsAsync = getProjectDiagnosticsAsync; this.CancellationToken = cancellationToken; }
//todo: remove when FixAllContext get a public ctor, what should be soon public static FixAllContext NewFixAllContext(Document document, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, string codeActionEquivalenceKey, IEnumerable<string> diagnosticIds, Func<Document, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getDocumentDiagnosticsAsync, Func<Project, bool, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getProjectDiagnosticsAsync, CancellationToken cancellationToken) { var parameters = new object[] { document, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync, cancellationToken }; var fixallContext = (FixAllContext)Activator.CreateInstance(typeof(FixAllContext), BindingFlags.NonPublic | BindingFlags.Instance, null, parameters, null); return fixallContext; }
public void VerifyCodeActions( IEnumerable<string> expectedItems, string applyFix = null, bool verifyNotShowing = false, bool ensureExpectedItemsAreOrdered = false, FixAllScope? fixAllScope = null) { Editor.ShowLightBulb(); Editor.WaitForLightBulbSession(); if (verifyNotShowing) { VerifyCodeActionsNotShowing(); return; } var actions = Editor.GetLightBulbActions(); if (expectedItems != null && expectedItems.Any()) { if (ensureExpectedItemsAreOrdered) { TestUtilities.ThrowIfExpectedItemNotFoundInOrder( actions, expectedItems); } else { TestUtilities.ThrowIfExpectedItemNotFound( actions, expectedItems); } } if (!string.IsNullOrEmpty(applyFix) || fixAllScope.HasValue) { Editor.ApplyLightBulbAction(applyFix, fixAllScope); // wait for action to complete WaitForAsyncOperations(FeatureAttribute.LightBulb); } }
public void VerifyCodeAction( string expectedItem, bool applyFix = false, bool verifyNotShowing = false, bool ensureExpectedItemsAreOrdered = false, FixAllScope? fixAllScope = null) { VerifyCodeActions(new[] { expectedItem }, expectedItem, verifyNotShowing, ensureExpectedItemsAreOrdered, fixAllScope); }
private async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, ImmutableArray <CodeFixProvider> codeFixProviders, int?codeFixIndex, string codeFixEquivalenceKey, Project project, int numberOfIterations, IVerifier verifier, CancellationToken cancellationToken) { var expectedNumberOfIterations = numberOfIterations; if (numberOfIterations < 0) { numberOfIterations = -numberOfIterations; } var previousDiagnostics = ImmutableArray.Create <Diagnostic>(); bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, CompilerDiagnostics, cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } verifier.False(--numberOfIterations < -1, "The upper limit for the number of fix all iterations was exceeded"); Diagnostic firstDiagnostic = null; CodeFixProvider effectiveCodeFixProvider = null; string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { var actions = new List <(CodeAction, CodeFixProvider)>(); foreach (var codeFixProvider in codeFixProviders) { if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)) { // do not pass unsupported diagnostics to a code fix provider continue; } var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add((a, codeFixProvider)), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); } var actionToApply = TryGetCodeActionToApply(actions.Select(a => a.Item1).ToList(), codeFixIndex, codeFixEquivalenceKey, verifier); if (actionToApply != null) { firstDiagnostic = diagnostic; effectiveCodeFixProvider = actions.SingleOrDefault(a => a.Item1 == actionToApply).Item2; equivalenceKey = actionToApply.EquivalenceKey; break; } } var fixAllProvider = effectiveCodeFixProvider?.GetFixAllProvider(); if (firstDiagnostic == null || fixAllProvider == null) { numberOfIterations++; break; } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); var analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id); var compilerDiagnosticIds = codeFixProviders.SelectMany(codeFixProvider => codeFixProvider.FixableDiagnosticIds).Where(x => x.StartsWith("CS", StringComparison.Ordinal) || x.StartsWith("BC", StringComparison.Ordinal)); var disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key); var relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct(); var fixAllContext = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), effectiveCodeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return(project); } var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false); if (fixedProject != project) { done = false; project = await RecreateProjectDocumentsAsync(fixedProject, verifier, cancellationToken).ConfigureAwait(false); } }while (!done); if (expectedNumberOfIterations >= 0) { verifier.Equal(expectedNumberOfIterations, expectedNumberOfIterations - numberOfIterations, $"Expected '{expectedNumberOfIterations}' iterations but found '{expectedNumberOfIterations - numberOfIterations}' iterations."); } else { verifier.True(numberOfIterations >= 0, "The upper limit for the number of code fix iterations was exceeded"); } return(project); }
private static async Task FixAllByScopeAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, IReadOnlyList <string> fixedCode, string fixTitle, AllowCompilationErrors allowCompilationErrors, Solution solution, FixAllScope scope) { VerifyCodeFixSupportsAnalyzer(analyzer, codeFix); var fixedSolution = await Fix.ApplyAllFixableScopeByScopeAsync(solution, analyzer, codeFix, scope, fixTitle, CancellationToken.None).ConfigureAwait(false); await AreEqualAsync(fixedCode, fixedSolution, $"Applying fixes for {scope} failed.").ConfigureAwait(false); if (allowCompilationErrors == AllowCompilationErrors.No) { await VerifyNoCompilerErrorsAsync(codeFix, fixedSolution).ConfigureAwait(false); } }
public async Task WhenFixAllIsScopedToDocumentAndProject_ThenOnlyFixInScopeInsteadOfEverything_DifferentProjects(FixAllScope scope) { using (var host = GetHost(true)) { var originalIde0055Text = @" internal class InvalidFormatIDE0055ExpectedHere{} "; var expectedIde0055TextWithFixedFormat = @" internal class InvalidFormatIDE0055ExpectedHere { } "; var fileInScope = CreateTestProjectWithDocument(host, originalIde0055Text); var fileNotInScope = CreateTestProjectWithDocument(host, originalIde0055Text); var handler = host.GetRequestHandler <RunFixAllCodeActionService>(OmniSharpEndpoints.RunFixAll); await handler.Handle(new RunFixAllRequest { Scope = scope, FileName = fileInScope, FixAllFilter = new[] { new FixAllItem("IDE0055", "Fix formatting") }, WantsTextChanges = true, WantsAllCodeActionOperations = true }); string textAfterFixInScope = await GetContentOfDocumentFromWorkspace(host, fileInScope); string textAfterNotInScope = await GetContentOfDocumentFromWorkspace(host, fileNotInScope); AssertUtils.AssertIgnoringIndent(expectedIde0055TextWithFixedFormat, textAfterFixInScope); AssertUtils.AssertIgnoringIndent(originalIde0055Text, textAfterNotInScope); } }
public void FixAllOccurrencesIgnoresGeneratedCode(FixAllScope scope) { var markup = @" using System; using $$System.Threading; class C { public IntPtr X1 { get; set; } }"; var expectedText = @" using System; class C { public IntPtr X1 { get; set; } }"; var generatedSourceMarkup = @"// <auto-generated/> using System; using $$System.Threading; class D { public IntPtr X1 { get; set; } }"; var expectedGeneratedSource = @"// <auto-generated/> using System; class D { public IntPtr X1 { get; set; } }"; MarkupTestFile.GetPosition(generatedSourceMarkup, out var generatedSource, out int generatedSourcePosition); VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), "D.cs", generatedSource, open: false); // Switch to the main document we'll be editing VisualStudio.SolutionExplorer.OpenFile(new ProjectUtils.Project(ProjectName), "Class1.cs"); // Verify that applying a Fix All operation does not change generated files. // This is a regression test for correctness with respect to the design. SetUpEditor(markup); VisualStudio.WaitForApplicationIdle(CancellationToken.None); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Remove Unnecessary Usings", applyFix: true, fixAllScope: scope); Assert.Equal(expectedText, VisualStudio.Editor.GetText()); VisualStudio.SolutionExplorer.OpenFile(new ProjectUtils.Project(ProjectName), "D.cs"); Assert.Equal(generatedSource, VisualStudio.Editor.GetText()); // Verify that a Fix All in Document in the generated file still does nothing. // ⚠ This is a statement of the current behavior, and not a claim regarding correctness of the design. // The current behavior is observable; any change to this behavior should be part of an intentional design // change. VisualStudio.Editor.MoveCaret(generatedSourcePosition); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Remove Unnecessary Usings", applyFix: true, fixAllScope: FixAllScope.Document); Assert.Equal(generatedSource, VisualStudio.Editor.GetText()); // Verify that the code action can still be applied manually from within the generated file. // This is a regression test for correctness with respect to the design. VisualStudio.Editor.MoveCaret(generatedSourcePosition); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Remove Unnecessary Usings", applyFix: true, fixAllScope: null); Assert.Equal(expectedGeneratedSource, VisualStudio.Editor.GetText()); }
private async Task TestFixAllAsync(int codepage, FixAllScope scope) { string[] testCode = new[] { "class Foo { }", "class Bar { }" }; this.fileEncoding = Encoding.GetEncoding(codepage); // Create a project using the specified encoding Project project = this.CreateProject(testCode); Project oldProject = project; Workspace workspace = project.Solution.Workspace; var codeFixer = this.GetCSharpCodeFixProvider(); var fixAllProvider = codeFixer.GetFixAllProvider(); var diagnostics = new List<Diagnostic>(); var descriptor = this.GetCSharpDiagnosticAnalyzers().First().SupportedDiagnostics.First(); foreach (var document in project.Documents) { // Create a diagnostic for the document to fix var diagnostic = Diagnostic.Create(descriptor, Location.Create(await document.GetSyntaxTreeAsync().ConfigureAwait(false), TextSpan.FromBounds(0, 0))); diagnostics.Add(diagnostic); } FixAllContext fixAllContext = new FixAllContext(project.Documents.First(), codeFixer, scope, nameof(SA1412CodeFixProvider) + "." + this.fileEncoding.WebName, new[] { SA1412StoreFilesAsUtf8.DiagnosticId }, TestDiagnosticProvider.Create(diagnostics.ToImmutableArray()), CancellationToken.None); CodeAction codeAction = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); var operation = codeAction.GetOperationsAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()[0]; operation.Apply(workspace, CancellationToken.None); // project should now have the "fixed document" in it. // Because of limitations in roslyn the fixed document should // have a different DocumentId then the broken document project = workspace.CurrentSolution.Projects.First(); Assert.Equal(2, project.DocumentIds.Count); for (int i = 0; i < project.DocumentIds.Count; i++) { DocumentId documentId = project.DocumentIds[i]; SourceText sourceText = await project.GetDocument(documentId).GetTextAsync().ConfigureAwait(false); Assert.Equal(testCode[i], sourceText.ToString()); Assert.Equal(Encoding.UTF8, sourceText.Encoding); Assert.NotEqual(oldProject.DocumentIds[i], project.DocumentIds[i]); } }
private static FixAllSuggestedAction GetFixAllSuggestedAction(IEnumerable <SuggestedActionSet> actionSets, FixAllScope fixAllScope) { foreach (var actionSet in actionSets) { foreach (var action in actionSet.Actions) { if (action is FixAllSuggestedAction fixAllSuggestedAction) { var fixAllCodeAction = fixAllSuggestedAction.CodeAction as FixSomeCodeAction; if (fixAllCodeAction?.FixAllState?.Scope == fixAllScope) { return(fixAllSuggestedAction); } } if (action.HasActionSets) { var nestedActionSets = HostWaitHelper.PumpingWaitResult(action.GetActionSetsAsync(CancellationToken.None)); fixAllSuggestedAction = GetFixAllSuggestedAction(nestedActionSets, fixAllScope); if (fixAllSuggestedAction != null) { return(fixAllSuggestedAction); } } } } return(null); }
protected abstract Task <ImmutableDictionary <Document, ImmutableArray <TextSpan> > > GetFixAllSpansIfWithinGlobalStatementAsync( Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken);
private static async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken) { int expectedNumberOfIterations = numberOfIterations; if (numberOfIterations < 0) { numberOfIterations = -numberOfIterations; } var previousDiagnostics = ImmutableArray.Create <Diagnostic>(); var fixAllProvider = codeFixProvider.GetFixAllProvider(); if (fixAllProvider == null) { return(null); } bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } if (--numberOfIterations < 0) { Assert.True(false, "The upper limit for the number of fix all iterations was exceeded"); } Diagnostic firstDiagnostic = null; string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)) { // do not pass unsupported diagnostics to a code fix provider continue; } var actions = new List <CodeAction>(); var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (actions.Count > (codeFixIndex ?? 0)) { firstDiagnostic = diagnostic; equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey; break; } } if (firstDiagnostic == null) { return(project); } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); IEnumerable <string> analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id); IEnumerable <string> compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal)); IEnumerable <string> disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key); IEnumerable <string> relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct(); FixAllContext fixAllContext = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return(project); } var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false); if (fixedProject != project) { done = false; project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false); } }while (!done); if (expectedNumberOfIterations >= 0) { Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations"); } return(project); }
private static FixAllState GetFixAllState( FixAllProvider fixAllProvider, IEnumerable<Diagnostic> diagnostics, DiagnosticAnalyzer provider, CodeFixProvider fixer, TestDiagnosticAnalyzerDriver testDriver, Document document, FixAllScope scope, string fixAllActionId) { Assert.NotEmpty(diagnostics); if (scope == FixAllScope.Custom) { // Bulk fixing diagnostics in selected scope. var diagnosticsToFix = ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable(KeyValuePair.Create(document, diagnostics.ToImmutableArray()))); return FixAllState.Create(fixAllProvider, diagnosticsToFix, fixer, fixAllActionId); } var diagnostic = diagnostics.First(); Func<Document, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getDocumentDiagnosticsAsync = async (d, diagIds, c) => { var root = await d.GetSyntaxRootAsync(); var diags = await testDriver.GetDocumentDiagnosticsAsync(provider, d, root.FullSpan); diags = diags.Where(diag => diagIds.Contains(diag.Id)); return diags; }; Func<Project, bool, ImmutableHashSet<string>, CancellationToken, Task<IEnumerable<Diagnostic>>> getProjectDiagnosticsAsync = async (p, includeAllDocumentDiagnostics, diagIds, c) => { var diags = includeAllDocumentDiagnostics ? await testDriver.GetAllDiagnosticsAsync(provider, p) : await testDriver.GetProjectDiagnosticsAsync(provider, p); diags = diags.Where(diag => diagIds.Contains(diag.Id)); return diags; }; var diagnosticIds = ImmutableHashSet.Create(diagnostic.Id); var fixAllDiagnosticProvider = new FixAllState.FixAllDiagnosticProvider(diagnosticIds, getDocumentDiagnosticsAsync, getProjectDiagnosticsAsync); return diagnostic.Location.IsInSource ? new FixAllState(fixAllProvider, document, fixer, scope, fixAllActionId, diagnosticIds, fixAllDiagnosticProvider) : new FixAllState(fixAllProvider, document.Project, fixer, scope, fixAllActionId, diagnosticIds, fixAllDiagnosticProvider); }
private static async Task<Document> GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray<DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int? codeFixIndex, Document document, int maxNumberOfIterations, CancellationToken cancellationToken) { var previousDiagnostics = ImmutableArray.Create<Diagnostic>(); var fixAllProvider = codeFixProvider.GetFixAllProvider(); if (fixAllProvider == null) { return null; } bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } if (--maxNumberOfIterations < 0) { Assert.True(false, "The upper limit for the number of fix all iterations was exceeded"); } string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { var actions = new List<CodeAction>(); var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (actions.Count > (codeFixIndex ?? 0)) { equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey; break; } } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); FixAllContext fixAllContext = new FixAllContext(document, codeFixProvider, scope, equivalenceKey, codeFixProvider.FixableDiagnosticIds, fixAllDiagnosticProvider, cancellationToken); CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return document; } var fixedDocument = await ApplyFixAsync(document, action, cancellationToken).ConfigureAwait(false); if (fixedDocument != document) { done = false; var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document. document = document.WithText(newText); } } while (!done); return document; }
private static async Task <GetFixAllResponse> GetFixAllTargets(OmniSharpTestHost host, string fileName, FixAllScope scope) { var handler = host.GetRequestHandler <GetFixAllCodeActionService>(OmniSharpEndpoints.GetFixAll); return(await handler.Handle(new GetFixAllRequest() { FileName = fileName, Scope = scope })); }