File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\LanguageServices\CSharpAddImportsService.cs
Web Access
Project: src\src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\CSharp\Roslyn.Diagnostics.CSharp.Analyzers.csproj (Roslyn.Diagnostics.CSharp.Analyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
 
namespace Microsoft.CodeAnalysis.CSharp.AddImport;
 
[ExportLanguageService(typeof(IAddImportsService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpAddImportsService() : AbstractAddImportsService<
    CompilationUnitSyntax, BaseNamespaceDeclarationSyntax, UsingDirectiveSyntax, ExternAliasDirectiveSyntax>
{
    protected override string Language
        => LanguageNames.CSharp;
 
    public override CodeStyleOption2<AddImportPlacement> GetUsingDirectivePlacementCodeStyleOption(IOptionsReader configOptions)
        => configOptions.GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement);
 
    protected override ImmutableArray<SyntaxNode> GetGlobalImports(
        SemanticModel semanticModel, SyntaxNode? contextLocation, SyntaxGenerator generator, CancellationToken cancellationToken)
    {
        using var result = TemporaryArray<SyntaxNode>.Empty;
        if (semanticModel != null)
        {
            var importScopes = semanticModel.GetImportScopes(contextLocation?.SpanStart ?? 0, cancellationToken);
 
            foreach (var scope in importScopes)
            {
                foreach (var usingNode in scope.Imports)
                {
                    if (usingNode.DeclaringSyntaxReference?.GetSyntax(cancellationToken) is UsingDirectiveSyntax usingDirective &&
                        usingDirective.GlobalKeyword != default)
                    {
                        result.Add(usingDirective.WithGlobalKeyword(default));
                    }
                }
            }
        }
 
        return result.ToImmutableAndClear();
    }
 
    protected override SyntaxNode? GetAlias(UsingDirectiveSyntax usingOrAlias)
        => usingOrAlias.Alias;
 
    protected override bool IsStaticUsing(UsingDirectiveSyntax usingOrAlias)
        => usingOrAlias.StaticKeyword != default;
 
    protected override SyntaxNode Rewrite(
        ExternAliasDirectiveSyntax[] externAliases,
        UsingDirectiveSyntax[] usingDirectives,
        UsingDirectiveSyntax[] staticUsingDirectives,
        UsingDirectiveSyntax[] aliasDirectives,
        SyntaxNode externContainer,
        SyntaxNode usingContainer,
        SyntaxNode staticUsingContainer,
        SyntaxNode aliasContainer,
        AddImportPlacementOptions options,
        SyntaxNode root,
        CancellationToken cancellationToken)
    {
        var rewriter = new Rewriter(
            externAliases, usingDirectives, staticUsingDirectives, aliasDirectives,
            externContainer, usingContainer, staticUsingContainer, aliasContainer,
            options, cancellationToken);
 
        var newRoot = rewriter.Visit(root);
        return newRoot;
    }
 
    protected override SyntaxList<UsingDirectiveSyntax> GetUsingsAndAliases(SyntaxNode node)
        => node switch
        {
            CompilationUnitSyntax c => c.Usings,
            BaseNamespaceDeclarationSyntax n => n.Usings,
            _ => default,
        };
 
    protected override SyntaxList<ExternAliasDirectiveSyntax> GetExterns(SyntaxNode node)
        => node switch
        {
            CompilationUnitSyntax c => c.Externs,
            BaseNamespaceDeclarationSyntax n => n.Externs,
            _ => default,
        };
 
    protected override bool IsEquivalentImport(SyntaxNode a, SyntaxNode b)
        => SyntaxFactory.AreEquivalent(a, b, kind => kind == SyntaxKind.NullableDirectiveTrivia);
 
    private sealed class Rewriter(
        ExternAliasDirectiveSyntax[] externAliases,
        UsingDirectiveSyntax[] usingDirectives,
        UsingDirectiveSyntax[] staticUsingDirectives,
        UsingDirectiveSyntax[] aliasDirectives,
        SyntaxNode externContainer,
        SyntaxNode usingContainer,
        SyntaxNode aliasContainer,
        SyntaxNode staticUsingContainer,
        AddImportPlacementOptions options,
        CancellationToken cancellationToken) : CSharpSyntaxRewriter
    {
        [return: NotNullIfNotNull(nameof(node))]
        public override SyntaxNode? Visit(SyntaxNode? node)
            => base.Visit(node);
 
        public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
            => VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitNamespaceDeclaration(node));
 
        public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node)
            => VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitFileScopedNamespaceDeclaration(node));
 
        private BaseNamespaceDeclarationSyntax VisitBaseNamespaceDeclaration(
            BaseNamespaceDeclarationSyntax node, BaseNamespaceDeclarationSyntax? rewritten)
        {
            Contract.ThrowIfNull(rewritten);
 
            // recurse downwards so we visit inner namespaces first.
            if (!node.CanAddUsingDirectives(options.AllowInHiddenRegions, cancellationToken))
                return rewritten;
 
            rewritten = node == aliasContainer ? rewritten.AddUsingDirectives(aliasDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == usingContainer ? rewritten.AddUsingDirectives(usingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == staticUsingContainer ? rewritten.AddUsingDirectives(staticUsingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == externContainer ? rewritten.AddExterns(externAliases) : rewritten;
 
            return rewritten;
        }
 
        public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
        {
            // recurse downwards so we visit inner namespaces first.
            var rewritten = (CompilationUnitSyntax)(base.VisitCompilationUnit(node) ?? throw ExceptionUtilities.Unreachable());
 
            if (!node.CanAddUsingDirectives(options.AllowInHiddenRegions, cancellationToken))
                return rewritten;
 
            rewritten = node == aliasContainer ? rewritten.AddUsingDirectives(aliasDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == usingContainer ? rewritten.AddUsingDirectives(usingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == staticUsingContainer ? rewritten.AddUsingDirectives(staticUsingDirectives, options.PlaceSystemNamespaceFirst) : rewritten;
            rewritten = node == externContainer ? rewritten.AddExterns(externAliases) : rewritten;
 
            return rewritten;
        }
    }
}