예제 #1
0
        /// <summary>
        /// Checks that the target framework of the current runner can load the extension assembly. For example, .NET Core
        /// cannot load .NET Framework assemblies and vice-versa.
        /// </summary>
        /// <param name="runnerAsm">The executing runner</param>
        /// <param name="extensionAsm">The extension we are attempting to load</param>
        internal static bool CanLoadTargetFramework(Assembly runnerAsm, ExtensionAssembly extensionAsm)
        {
            if (runnerAsm == null)
            {
                return(true);
            }

            var extHelper    = new TargetFrameworkHelper(extensionAsm.FilePath);
            var runnerHelper = new TargetFrameworkHelper(runnerAsm.Location);

            if (runnerHelper.FrameworkName?.StartsWith(".NETStandard") == true)
            {
                throw new NUnitEngineException($"{runnerAsm.FullName} test runner must target .NET Core or .NET Framework, not .NET Standard");
            }
            else if (runnerHelper.FrameworkName?.StartsWith(".NETCoreApp") == true)
            {
                if (extHelper.FrameworkName?.StartsWith(".NETStandard") != true && extHelper.FrameworkName?.StartsWith(".NETCoreApp") != true)
                {
                    log.Info($".NET Core runners require .NET Core or .NET Standard extension for {extensionAsm.FilePath}");
                    return(false);
                }
            }
            else if (extHelper.FrameworkName?.StartsWith(".NETCoreApp") == true)
            {
                log.Info($".NET Framework runners cannot load .NET Core extension {extensionAsm.FilePath}");
                return(false);
            }

            return(true);
        }
예제 #2
0
 public ExtensionAssembly(string filePath, bool fromWildCard)
 {
     FilePath               = filePath;
     FromWildCard           = fromWildCard;
     Assembly               = GetAssemblyDefinition();
     _targetFrameworkHelper = new TargetFrameworkHelper(Assembly);
 }
예제 #3
0
        /// <summary>
        /// Does actual query to Packag Index to find packages where a given type is defined.
        /// Note: At diagnostic level there no way to know in which document, project or workspace
        /// we are for a given SyntaxNode (or it's SyntaxNodeAnalysisContext) since diagnostics
        /// work at csc.exe level (in command line for example) and there no container objects defined
        /// at that time.
        /// Thus we will display information about all packages returned form index to suggest user where
        /// type can be located, however when user clicks Ctrl+. we would display only code fixes for packages
        /// that satisfy current project target frameworks list.
        /// </summary>
        protected override IEnumerable <string> AnalyzeNode(TIdentifierNameSyntax node)
        {
            var projectFilter = GetProjectFilter();

            if (!projectFilter.IsProjectSupported(node.GetLocation().SourceTree.FilePath))
            {
                return(null);
            }

            var typeName = node.ToString();
            var packagesWithGivenType = _packageSearcher.Search(typeName);

            if (!packagesWithGivenType.Any())
            {
                return(null);
            }

            var projectTargetFrameworks = _targetFrameworkProvider.GetTargetFrameworks(node.GetLocation().SourceTree.FilePath);

            // Note: allowHigherVersions=true here since we want to show diagnostic message for type if it exists in some package
            // for discoverability (tooltip) but we would not supply a code fix if package already installed in the project with
            // any version, user needs to upgrade on his own.
            // Note2: the problem here is that we don't know if type exist in older versions of the package or not and to store
            // all package versions in index might slow things down. If we receive feedback that we need ot be more smart here
            // we should consider adding all package versions to the local index.
            return(GetFriendlyPackagesString(TargetFrameworkHelper.GetSupportedPackages(packagesWithGivenType, projectTargetFrameworks, allowHigherVersions: true)
                                             .Take(MaxPackageSuggestions)));
        }
예제 #4
0
            static string GetBestTFM(BaselineProject baselineProject, bool keepCurrentTfm, string?specifiedTFM, bool usePreviewSDK)
            {
                if (string.IsNullOrWhiteSpace(specifiedTFM))
                {
                    // Let's figure this out, friends
                    var tfmForApps = TargetFrameworkHelper.FindHighestInstalledTargetFramework(usePreviewSDK);

                    if (keepCurrentTfm)
                    {
                        specifiedTFM = baselineProject.GetTfm();
                    }
                    else if (baselineProject.ProjectStyle == ProjectStyle.WindowsDesktop || baselineProject.ProjectStyle == ProjectStyle.MSTest)
                    {
                        specifiedTFM = tfmForApps;
                    }
                    else if (baselineProject.OutputType == ProjectOutputType.Library)
                    {
                        specifiedTFM = MSBuildFacts.Netstandard20;
                    }
                    else if (baselineProject.OutputType == ProjectOutputType.Exe)
                    {
                        specifiedTFM = tfmForApps;
                    }
                    else
                    {
                        // Default is to just use what exists in the project
                        specifiedTFM = baselineProject.GetTfm();
                    }
                }

                return(specifiedTFM);
            }
        /// <summary>
        /// Checks that the target framework of the current runner can load the extension assembly. For example, .NET Core
        /// cannot load .NET Framework assemblies and vice-versa.
        /// </summary>
        /// <param name="runnerAsm">The executing runner</param>
        /// <param name="extensionAsm">The extension we are attempting to load</param>
        internal static void ValidateTargetFramework(Assembly runnerAsm, ExtensionAssembly extensionAsm)
        {
            if (runnerAsm == null)
            {
                return;
            }

            var extHelper    = new TargetFrameworkHelper(extensionAsm.FilePath);
            var runnerHelper = new TargetFrameworkHelper(runnerAsm.Location);

            if (runnerHelper.FrameworkName?.StartsWith(".NETStandard") == true)
            {
                throw new NUnitEngineException("Test runners must target .NET Core or .NET Framework, not .NET Standard");
            }
            else if (runnerHelper.FrameworkName?.StartsWith(".NETCoreApp") == true)
            {
                if (extHelper.FrameworkName?.StartsWith(".NETStandard") != true && extHelper.FrameworkName?.StartsWith(".NETCoreApp") != true)
                {
                    throw new NUnitEngineException(".NET Core runners require .NET Core or .NET Standard extensions");
                }
            }
            else if (extHelper.FrameworkName?.StartsWith(".NETCoreApp") == true)
            {
                throw new NUnitEngineException(".NET Framework runners cannot load .NET Core extensions");
            }
        }
        /// <summary>
        /// Use Mono.Cecil to get information about an assembly and
        /// apply it to the package using special internal keywords.
        /// </summary>
        /// <param name="package"></param>
        static void ApplyImageSettings(TestPackage package)
        {
            Guard.ArgumentNotNull(package, nameof(package));

            var assembly = new TargetFrameworkHelper(package.FullName);

            var targetVersion = assembly.TargetRuntimeVersion;

            if (targetVersion.Major > 0)
            {
                log.Debug($"Assembly {package.FullName} uses version {targetVersion}");
                package.Settings[EnginePackageSettings.ImageRuntimeVersion] = targetVersion;
            }

            var frameworkName = assembly.FrameworkName;

            if (!string.IsNullOrEmpty(frameworkName))
            {
                log.Debug($"Assembly {package.FullName} targets {frameworkName}");
                package.Settings[EnginePackageSettings.ImageTargetFrameworkName] = frameworkName;
            }

            package.Settings[EnginePackageSettings.ImageRequiresX86] = assembly.RequiresX86;
            if (assembly.RequiresX86)
            {
                log.Debug($"Assembly {package.FullName} will be run x86");
                package.Settings[EnginePackageSettings.RunAsX86] = true;
            }

            package.Settings[EnginePackageSettings.ImageRequiresDefaultAppDomainAssemblyResolver] = assembly.RequiresAssemblyResolver;
            if (assembly.RequiresAssemblyResolver)
            {
                log.Debug($"Assembly {package.FullName} requires default app domain assembly resolver");
            }
        }
예제 #7
0
        public void InstallPackage(Workspace workspace,
                                   Document document,
                                   IPackageIndexModelInfo packageInfo,
                                   IEnumerable <ProjectMetadata> projects,
                                   CancellationToken cancellationToken = default(CancellationToken))
        {
            Debug.Assert(packageInfo != null);

            ThreadHelper.JoinableTaskFactory.RunAsync(async delegate {
                foreach (var project in projects)
                {
                    try
                    {
                        var container = _serviceProvider.GetService <IComponentModel, SComponentModel>();
                        var projectSpecificInstallers = container.DefaultExportProvider.GetExportedValues <IProjectPackageInstaller>();
                        if (projectSpecificInstallers != null && projectSpecificInstallers.Any())
                        {
                            var supportedInstaller = projectSpecificInstallers.FirstOrDefault(x => x.SupportsProject(project.ProjectPath));
                            if (supportedInstaller != null)
                            {
                                if (await SafeExecuteActionAsync(
                                        delegate
                                {
                                    var frameworksToInstall = new List <FrameworkName>();
                                    foreach (var projectFrameworkMetadata in project.TargetFrameworks)
                                    {
                                        if (TargetFrameworkHelper.AreCompatible(projectFrameworkMetadata, packageInfo.TargetFrameworks))
                                        {
                                            frameworksToInstall.Add(TargetFrameworkHelper.GetFrameworkName(projectFrameworkMetadata.TargetFrameworkShortName));
                                        }
                                    }

                                    return(supportedInstaller.InstallPackageAsync(project.ProjectPath,
                                                                                  packageInfo.PackageName,
                                                                                  packageInfo.PackageVersion,
                                                                                  frameworksToInstall,
                                                                                  cancellationToken));
                                }))
                                {
                                    continue; // package installed successfully
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // we should not throw here, since it would create an exception that may be
                        // visible to the user, instead just dump into debugger output or to package
                        // manager console.
                        // TODO Package manager console?
                        Debug.Write(e.ToString());
                    }
                }
            });
        }
예제 #8
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var document          = context.Document;
            var span              = context.Span;
            var diagnostics       = context.Diagnostics;
            var cancellationToken = context.CancellationToken;
            var project           = document.Project;
            var diagnostic        = diagnostics.First();
            var root              = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token     = root.FindToken(span.Start, findInsideTrivia: true);
            var ancestors = token.GetAncestors <SyntaxNode>();

            if (!ancestors.Any())
            {
                return;
            }

            var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root);

            if (node == null)
            {
                return;
            }

            var placeSystemNamespaceFirst = true;

            if (!cancellationToken.IsCancellationRequested)
            {
                if (CanAddImport(node, cancellationToken))
                {
                    var typeName = node.ToString();
                    var projectTargetFrameworks = _targetFrameworkProvider.GetTargetFrameworks(document.FilePath);
                    // Note: allowHigherVersions=false here since we don't want to provide code fix that adds another
                    // dependency for the same package but different version, user should upgrade it on his own when
                    // see Diagnostic suggestion
                    var packagesWithGivenType = TargetFrameworkHelper.GetSupportedPackages(_packageSearcher.Search(typeName), projectTargetFrameworks, allowHigherVersions: false)
                                                .Take(MaxPackageSuggestions);

                    foreach (var typeInfo in packagesWithGivenType)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        var namespaceName = typeInfo.FullName.Contains(".") ? Path.GetFileNameWithoutExtension(typeInfo.FullName) : null;
                        if (!string.IsNullOrEmpty(namespaceName))
                        {
                            var action = new AddPackageCodeAction(_packageInstaller, typeInfo, ActionTitle,
                                                                  (c) => AddImportAsync(node, namespaceName, document, placeSystemNamespaceFirst, cancellationToken));
                            context.RegisterCodeFix(action, diagnostic);
                        }
                    }
                }
            }
        }
예제 #9
0
        public static int Run(string?project, string?workspace, string?msbuildPath, string?tfm, bool allowPreviews, bool diffOnly, bool noBackup, bool keepCurrentTfms)
        {
            if (!string.IsNullOrWhiteSpace(project) && !string.IsNullOrWhiteSpace(workspace))
            {
                Console.WriteLine("Cannot specify both a project and a workspace.");
                return(-1);
            }

            if (!string.IsNullOrWhiteSpace(tfm) && keepCurrentTfms)
            {
                Console.WriteLine($"Both '{nameof(tfm)}' and '{nameof(keepCurrentTfms)}' cannot be specified. Please pick one.");
                return(-1);
            }

            try
            {
                msbuildPath = MSBuildHelpers.HookAssemblyResolveForMSBuild(msbuildPath);
                if (string.IsNullOrWhiteSpace(msbuildPath))
                {
                    Console.WriteLine("Could not find an MSBuild.");
                    return(-1);
                }

                if (!string.IsNullOrWhiteSpace(tfm))
                {
                    tfm = tfm.Trim();
                    if (!TargetFrameworkHelper.IsValidTargetFramework(tfm))
                    {
                        Console.WriteLine($"Invalid framework specified for --target-framework: '{tfm}'");
                        return(-1);
                    }
                }

                var workspacePath = string.Empty;
                MSBuildConversionWorkspaceType workspaceType;

                if (!string.IsNullOrWhiteSpace(project))
                {
                    workspacePath = Path.GetFullPath(project, Environment.CurrentDirectory);
                    workspaceType = MSBuildConversionWorkspaceType.Project;
                }
                else
                {
                    var(isSolution, workspaceFilePath) = MSBuildConversionWorkspaceFinder.FindWorkspace(Environment.CurrentDirectory, workspace);
                    workspaceType = isSolution ? MSBuildConversionWorkspaceType.Solution : MSBuildConversionWorkspaceType.Project;
                    workspacePath = workspaceFilePath;
                }

                var workspaceLoader = new MSBuildConversionWorkspaceLoader(workspacePath, workspaceType);
                // do not create backup if --diff-only specified
                noBackup = noBackup || diffOnly;
                var msbuildWorkspace = workspaceLoader.LoadWorkspace(workspacePath, noBackup);

                foreach (var item in msbuildWorkspace.WorkspaceItems)
                {
                    if (diffOnly)
                    {
                        var differ = new Differ(item.UnconfiguredProject.FirstConfiguredProject, item.SdkBaselineProject.Project.FirstConfiguredProject);
                        differ.GenerateReport(Directory.GetParent(workspacePath).FullName);
                    }
                    else
                    {
                        var converter = new Converter(item.UnconfiguredProject, item.SdkBaselineProject, item.ProjectRootElement);
                        converter.Convert(item.ProjectRootElement.FullPath, tfm, keepCurrentTfms, allowPreviews);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                return(-1);
            }

            Console.WriteLine("Conversion complete!");
            return(0);
        }
        /// <summary>
        /// Use Mono.Cecil to get information about all assemblies and
        /// apply it to the package using special internal keywords.
        /// </summary>
        /// <param name="package"></param>
        private static void ApplyImageData(TestPackage package)
        {
            string packageName = package.FullName;

            Version targetVersion            = new Version(0, 0);
            string  frameworkName            = null;
            bool    requiresX86              = false;
            bool    requiresAssemblyResolver = false;

            // We are doing two jobs here: (1) in the else clause (below)
            // we get information about a single assembly and record it,
            // (2) in the if clause, we recursively examine all subpackages
            // and then apply policies for promulgating each setting to
            // a containing package. We could implement the policy part at
            // a higher level, but it seems simplest to do it right here.
            if (package.SubPackages.Count > 0)
            {
                foreach (var subPackage in package.SubPackages)
                {
                    ApplyImageData(subPackage);

                    // Collect the highest version required
                    Version v = subPackage.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, new Version(0, 0));
                    if (v > targetVersion)
                    {
                        targetVersion = v;
                    }

                    // Collect highest framework name
                    // TODO: This assumes lexical ordering is valid - check it
                    string fn = subPackage.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, "");
                    if (fn != "")
                    {
                        if (frameworkName == null || fn.CompareTo(frameworkName) < 0)
                        {
                            frameworkName = fn;
                        }
                    }

                    // If any assembly requires X86, then the aggregate package requires it
                    if (subPackage.GetSetting(InternalEnginePackageSettings.ImageRequiresX86, false))
                    {
                        requiresX86 = true;
                    }

                    if (subPackage.GetSetting(InternalEnginePackageSettings.ImageRequiresDefaultAppDomainAssemblyResolver, false))
                    {
                        requiresAssemblyResolver = true;
                    }
                }
            }
            else if (File.Exists(packageName) && PathUtils.IsAssemblyFileType(packageName))
            {
                var assembly = new TargetFrameworkHelper(packageName);

                targetVersion = assembly.TargetRuntimeVersion;
                log.Debug($"Assembly {packageName} uses version {targetVersion}");

                frameworkName = assembly.FrameworkName;
                log.Debug($"Assembly {packageName} targets {frameworkName}");

                if (assembly.RequiresX86)
                {
                    requiresX86 = true;
                    log.Debug($"Assembly {packageName} will be run x86");
                }

                if (assembly.RequiresAssemblyResolver)
                {
                    requiresAssemblyResolver = true;
                    log.Debug($"Assembly {packageName} requires default app domain assembly resolver");
                }
            }

            if (targetVersion.Major > 0)
            {
                package.Settings[InternalEnginePackageSettings.ImageRuntimeVersion] = targetVersion;
            }

            if (!string.IsNullOrEmpty(frameworkName))
            {
                package.Settings[InternalEnginePackageSettings.ImageTargetFrameworkName] = frameworkName;
            }

            package.Settings[InternalEnginePackageSettings.ImageRequiresX86] = requiresX86;
            if (requiresX86)
            {
                package.Settings[EnginePackageSettings.RunAsX86] = true;
            }

            package.Settings[InternalEnginePackageSettings.ImageRequiresDefaultAppDomainAssemblyResolver] = requiresAssemblyResolver;
        }
예제 #11
0
        public static int Run(string?project, string?workspace, string?msbuildPath, string?tfm, bool forceWebConversion, bool preview, bool diffOnly, bool noBackup, bool keepCurrentTfms, bool update, bool mauiConversion, bool forceRemoveCustomImports)
        {
            if (update)
            {
                UpdateTryConvert.Update();
                return(0);
            }

            if (!string.IsNullOrWhiteSpace(project) && !string.IsNullOrWhiteSpace(workspace))
            {
                Console.WriteLine("Cannot specify both a project and a workspace.");
                return(-1);
            }

            if (!string.IsNullOrWhiteSpace(tfm) && keepCurrentTfms)
            {
                Console.WriteLine($"Both '{nameof(tfm)}' and '{nameof(keepCurrentTfms)}' cannot be specified. Please pick one.");
                return(-1);
            }

            try
            {
                //For Xamarin Projects, set MSBuild path to VSInstallation Dir via Environment Variable
                if (mauiConversion)
                {
                    var vsinstalldir = Environment.GetEnvironmentVariable("VSINSTALLDIR");
                    if (!string.IsNullOrEmpty(vsinstalldir))
                    {
                        msbuildPath = MSBuildHelpers.HookAssemblyResolveForMSBuild(Path.Combine(vsinstalldir, "MSBuild", "Current", "Bin"));
                    }
                    else
                    {
                        string vsPath = new VisualStudioLocator().GetLatestVisualStudioPath();
                        if (string.IsNullOrWhiteSpace(vsPath))
                        {
                            Console.WriteLine("Error locating VS Install Directory. Try setting Environment Variable VSINSTALLDIR.");
                            return(-1);
                        }
                        else
                        {
                            msbuildPath = MSBuildHelpers.HookAssemblyResolveForMSBuild(Path.Combine(vsPath, "MSBuild", "Current", "Bin"));
                        }
                    }
                }
                else
                {
                    msbuildPath = MSBuildHelpers.HookAssemblyResolveForMSBuild(msbuildPath);
                }

                if (string.IsNullOrWhiteSpace(msbuildPath))
                {
                    Console.WriteLine("Could not find an MSBuild.");
                    return(-1);
                }

                if (!string.IsNullOrWhiteSpace(tfm))
                {
                    tfm = tfm.Trim();
                    if (!TargetFrameworkHelper.IsValidTargetFramework(tfm))
                    {
                        Console.WriteLine($"Invalid framework specified for --target-framework: '{tfm}'");
                        return(-1);
                    }
                }
                else
                {
                    tfm = TargetFrameworkHelper.FindHighestInstalledTargetFramework(preview, msbuildPath);
                }

                var workspacePath = string.Empty;
                MSBuildConversionWorkspaceType workspaceType;

                if (!string.IsNullOrWhiteSpace(project))
                {
                    workspacePath = Path.GetFullPath(project, Environment.CurrentDirectory);
                    workspaceType = MSBuildConversionWorkspaceType.Project;
                }
                else
                {
                    var(isSolution, workspaceFilePath) = MSBuildConversionWorkspaceFinder.FindWorkspace(Environment.CurrentDirectory, workspace);
                    workspaceType = isSolution ? MSBuildConversionWorkspaceType.Solution : MSBuildConversionWorkspaceType.Project;
                    workspacePath = workspaceFilePath;
                }

                var workspaceLoader = new MSBuildConversionWorkspaceLoader(workspacePath, workspaceType);
                // do not create backup if --diff-only specified
                noBackup = noBackup || diffOnly;
                var msbuildWorkspace = workspaceLoader.LoadWorkspace(workspacePath, noBackup, tfm, keepCurrentTfms, forceWebConversion);

                if (msbuildWorkspace.WorkspaceItems.Length is 0)
                {
                    Console.WriteLine("No projects converted.");
                    return(0);
                }

                foreach (var item in msbuildWorkspace.WorkspaceItems)
                {
                    if (diffOnly)
                    {
                        var differ = new Differ(item.UnconfiguredProject.FirstConfiguredProject, item.SdkBaselineProject.Project.FirstConfiguredProject);
                        var parent = Directory.GetParent(workspacePath);
                        if (parent is null)
                        {
                            differ.GenerateReport(workspacePath);
                        }
                        else
                        {
                            differ.GenerateReport(parent.FullName);
                        }
                    }
                    else
                    {
                        var converter = new Converter(item.UnconfiguredProject, item.SdkBaselineProject, item.ProjectRootElement, noBackup, forceRemoveCustomImports);
                        converter.Convert(item.ProjectRootElement.FullPath);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                return(-1);
            }

            Console.WriteLine("Conversion complete!");
            return(0);
        }
예제 #12
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var document = context.Document;

            var projects = _projectMetadataProvider.GetProjects(document.FilePath);

            if (projects == null || !projects.Any())
            {
                // project is unsupported
                return;
            }

            var span              = context.Span;
            var diagnostics       = context.Diagnostics;
            var cancellationToken = context.CancellationToken;
            var project           = document.Project;
            var diagnostic        = diagnostics.First();
            var root              = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token     = root.FindToken(span.Start, findInsideTrivia: true);
            var ancestors = token.GetAncestors <SyntaxNode>();

            if (!ancestors.Any())
            {
                return;
            }

            var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root);

            if (node == null)
            {
                return;
            }

            var placeSystemNamespaceFirst = true;

            if (!cancellationToken.IsCancellationRequested)
            {
                // get distinct frameworks from all projects current file belongs to
                var suggestions = Analyzer.GetSuggestions(node, projects);
                foreach (var packageInfo in suggestions.Take(MaxPackageSuggestions))
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var namespaceName           = packageInfo.GetNamespace();
                    AddPackageCodeAction action = null;
                    if (string.IsNullOrEmpty(namespaceName))
                    {
                        // namspaces suggestions don't need to add another namespace
                        action = new AddPackageCodeAction(_packageInstaller,
                                                          packageInfo,
                                                          projects.Where(x => TargetFrameworkHelper.SupportsProjectTargetFrameworks(packageInfo, x.TargetFrameworks)).ToList(),
                                                          ActionTitle,
                                                          (c) => Task.FromResult(document));
                    }
                    else if (CanAddImport(node, cancellationToken))
                    {
                        action = new AddPackageCodeAction(_packageInstaller,
                                                          packageInfo,
                                                          projects.Where(x => TargetFrameworkHelper.SupportsProjectTargetFrameworks(packageInfo, x.TargetFrameworks)).ToList(),
                                                          ActionTitle,
                                                          (c) => AddImportAsync(node, namespaceName, document, placeSystemNamespaceFirst, cancellationToken));
                    }

                    context.RegisterCodeFix(action, diagnostic);
                }
            }
        }