// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeFixes { /// /// Context for code fixes provided by a . /// public struct CodeFixContext { private readonly Document _document; private readonly Project _project; private readonly TextSpan _span; private readonly ImmutableArray _diagnostics; private readonly CancellationToken _cancellationToken; private readonly Action> _registerCodeFix; /// /// Document corresponding to the to fix. /// public Document Document => _document; /// /// Project corresponding to the diagnostics to fix. /// internal Project Project => _project; /// /// Text span within the to fix. /// public TextSpan Span => _span; /// /// Diagnostics to fix. /// NOTE: All the diagnostics in this collection have the same . /// public ImmutableArray Diagnostics => _diagnostics; /// /// CancellationToken. /// public CancellationToken CancellationToken => _cancellationToken; /// /// Creates a code fix context to be passed into method. /// /// Document to fix. /// Text span within the to fix. /// /// Diagnostics to fix. /// All the diagnostics must have the same . /// Additionally, the of each diagnostic must be in the set of the of the associated . /// /// Delegate to register a fixing a subset of diagnostics. /// Cancellation token. /// Throws this exception if any of the arguments is null. /// /// Throws this exception if the given is empty, /// has a null element or has an element whose span is not equal to . /// public CodeFixContext( Document document, TextSpan span, ImmutableArray diagnostics, Action> registerCodeFix, CancellationToken cancellationToken) : this(document, span, diagnostics, registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken) { } /// /// Creates a code fix context to be passed into method. /// /// Document to fix. /// /// Diagnostic to fix. /// The of this diagnostic must be in the set of the of the associated . /// /// Delegate to register a fixing a subset of diagnostics. /// Cancellation token. /// Throws this exception if any of the arguments is null. public CodeFixContext( Document document, Diagnostic diagnostic, Action> registerCodeFix, CancellationToken cancellationToken) : this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments: true, cancellationToken: cancellationToken) { } internal CodeFixContext( Document document, TextSpan span, ImmutableArray diagnostics, Action> registerCodeFix, bool verifyArguments, CancellationToken cancellationToken) : this(document, document.Project, span, diagnostics, registerCodeFix, verifyArguments, cancellationToken) { } internal CodeFixContext( Project project, ImmutableArray diagnostics, Action> registerCodeFix, CancellationToken cancellationToken) : this(document: null, project: project, span: default, diagnostics: diagnostics, registerCodeFix: registerCodeFix, verifyArguments: false, cancellationToken: cancellationToken) { } private CodeFixContext( Document document, Project project, TextSpan span, ImmutableArray diagnostics, Action> registerCodeFix, bool verifyArguments, CancellationToken cancellationToken) { if (verifyArguments) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (registerCodeFix == null) { throw new ArgumentNullException(nameof(registerCodeFix)); } VerifyDiagnosticsArgument(diagnostics, span); } _document = document; _project = project; _span = span; _diagnostics = diagnostics; _registerCodeFix = registerCodeFix; _cancellationToken = cancellationToken; } internal CodeFixContext( Document document, Diagnostic diagnostic, Action> registerCodeFix, bool verifyArguments, CancellationToken cancellationToken) : this(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), registerCodeFix, verifyArguments, cancellationToken) { } /// /// Add supplied to the list of fixes that will be offered to the user. /// /// The that will be invoked to apply the fix. /// The subset of being addressed / fixed by the . public void RegisterCodeFix(CodeAction action, Diagnostic diagnostic) { if (action == null) { throw new ArgumentNullException(nameof(action)); } if (diagnostic == null) { throw new ArgumentNullException(nameof(diagnostic)); } _registerCodeFix(action, ImmutableArray.Create(diagnostic)); } /// /// Add supplied to the list of fixes that will be offered to the user. /// /// The that will be invoked to apply the fix. /// The subset of being addressed / fixed by the . public void RegisterCodeFix(CodeAction action, IEnumerable diagnostics) { if (diagnostics == null) { throw new ArgumentNullException(nameof(diagnostics)); } RegisterCodeFix(action, diagnostics.ToImmutableArray()); } /// /// Add supplied to the list of fixes that will be offered to the user. /// /// The that will be invoked to apply the fix. /// The subset of being addressed / fixed by the . public void RegisterCodeFix(CodeAction action, ImmutableArray diagnostics) { if (action == null) { throw new ArgumentNullException(nameof(action)); } VerifyDiagnosticsArgument(diagnostics, _span); // TODO: // - Check that all diagnostics are unique (no duplicates). // - Check that supplied diagnostics form subset of diagnostics originally // passed to the provider via CodeFixContext.Diagnostics. _registerCodeFix(action, diagnostics); } private static void VerifyDiagnosticsArgument(ImmutableArray diagnostics, TextSpan span) { if (diagnostics.IsDefault) { throw new ArgumentException(nameof(diagnostics)); } if (diagnostics.Length == 0) { throw new ArgumentException(WorkspacesResources.At_least_one_diagnostic_must_be_supplied, nameof(diagnostics)); } if (diagnostics.Any(d => d == null)) { throw new ArgumentException(WorkspacesResources.Supplied_diagnostic_cannot_be_null, nameof(diagnostics)); } if (diagnostics.Any(d => d.Location.SourceSpan != span)) { throw new ArgumentException(string.Format(WorkspacesResources.Diagnostic_must_have_span_0, span.ToString()), nameof(diagnostics)); } } } }