File: src\roslyn\src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\Extensions\ITypeSymbolExtensions.cs
Web Access
Project: src\roslyn\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.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;

namespace Microsoft.CodeAnalysis.CSharp.Extensions;

using static CSharpSyntaxTokens;
using static SyntaxFactory;

internal static partial class ITypeSymbolExtensions
{
    /// <paramref name="nameSyntax"><see langword="true"/> if only normal name-syntax nodes should be returned.
    /// <see langword="false"/> if special nodes (like predefined types) can be used.</paramref>
    public static ExpressionSyntax GenerateExpressionSyntax(this ITypeSymbol typeSymbol, bool nameSyntax = false)
        => typeSymbol.Accept(ExpressionSyntaxGeneratorVisitor.Create(nameSyntax))!.WithAdditionalAnnotations(Simplifier.Annotation);

    public static NameSyntax GenerateNameSyntax(this INamespaceOrTypeSymbol symbol, bool allowVar = true)
        => (NameSyntax)GenerateTypeSyntax(symbol, nameSyntax: true, allowVar: allowVar);

    public static TypeSyntax GenerateTypeSyntax(this INamespaceOrTypeSymbol symbol, bool allowVar = true)
        => GenerateTypeSyntax(symbol, nameSyntax: false, allowVar: allowVar);

    private static TypeSyntax GenerateTypeSyntax(
        INamespaceOrTypeSymbol symbol, bool nameSyntax, bool allowVar = true)
    {
        var type = symbol as ITypeSymbol;
        var containsAnonymousType = type != null && type.ContainsAnonymousType();

        // something with an anonymous type can only be represented with 'var', regardless
        // of what the user's preferences might be.
        if (containsAnonymousType && allowVar)
            return IdentifierName("var");

        var syntax = containsAnonymousType
            ? TypeSyntaxGeneratorVisitor.CreateSystemObject()
            : symbol.Accept(TypeSyntaxGeneratorVisitor.Create(nameSyntax))!.WithAdditionalAnnotations(Simplifier.Annotation);

        if (!allowVar)
            syntax = syntax.WithAdditionalAnnotations(DoNotAllowVarAnnotation.Annotation);

        if (type != null && type.IsReferenceType)
        {
            var additionalAnnotation = type.NullableAnnotation switch
            {
                NullableAnnotation.None => NullableSyntaxAnnotation.Oblivious,
                NullableAnnotation.Annotated => NullableSyntaxAnnotation.AnnotatedOrNotAnnotated,
                NullableAnnotation.NotAnnotated => NullableSyntaxAnnotation.AnnotatedOrNotAnnotated,
                _ => throw ExceptionUtilities.UnexpectedValue(type.NullableAnnotation),
            };

            if (additionalAnnotation is not null)
                syntax = syntax.WithAdditionalAnnotations(additionalAnnotation);
        }

        return syntax;
    }

    public static TypeSyntax GenerateRefTypeSyntax(
        this INamespaceOrTypeSymbol symbol, bool allowVar = true)
    {
        var underlyingType = GenerateTypeSyntax(symbol, allowVar)
            .WithPrependedLeadingTrivia(ElasticMarker)
            .WithAdditionalAnnotations(Simplifier.Annotation);
        var refKeyword = RefKeyword;
        return RefType(refKeyword, underlyingType);
    }

    public static TypeSyntax GenerateRefReadOnlyTypeSyntax(
        this INamespaceOrTypeSymbol symbol, bool allowVar = true)
    {
        var underlyingType = GenerateTypeSyntax(symbol, allowVar)
            .WithPrependedLeadingTrivia(ElasticMarker)
            .WithAdditionalAnnotations(Simplifier.Annotation);
        var refKeyword = RefKeyword;
        var readOnlyKeyword = ReadOnlyKeyword;
        return RefType(refKeyword, readOnlyKeyword, underlyingType);
    }

    public static bool ContainingTypesOrSelfHasUnsafeKeyword(this ITypeSymbol containingType)
    {
        do
        {
            foreach (var reference in containingType.DeclaringSyntaxReferences)
            {
                if (reference.GetSyntax().ChildTokens().Any(t => t.IsKind(SyntaxKind.UnsafeKeyword)))
                {
                    return true;
                }
            }

            containingType = containingType.ContainingType;
        }
        while (containingType != null);
        return false;
    }

    public static async Task<ISymbol?> FindApplicableAliasAsync(this ITypeSymbol type, int position, SemanticModel semanticModel, CancellationToken cancellationToken)
    {
        try
        {
            if (semanticModel.IsSpeculativeSemanticModel)
            {
                position = semanticModel.OriginalPositionForSpeculation;
                semanticModel = semanticModel.ParentModel;
            }

            var root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            var applicableUsings = GetApplicableUsings(position, (CompilationUnitSyntax)root);
            foreach (var applicableUsing in applicableUsings)
            {
                var alias = semanticModel.GetOriginalSemanticModel().GetDeclaredSymbol(applicableUsing, cancellationToken);
                if (alias != null && Equals(alias.Target, type))
                {
                    return alias;
                }
            }

            return null;
        }
        catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.General))
        {
            throw ExceptionUtilities.Unreachable();
        }
    }

    private static IEnumerable<UsingDirectiveSyntax> GetApplicableUsings(int position, SyntaxNode root)
    {
        var namespaceUsings = root.FindToken(position).Parent!.GetAncestors<BaseNamespaceDeclarationSyntax>().SelectMany(n => n.Usings);
        var allUsings = root is CompilationUnitSyntax compilationUnit
            ? compilationUnit.Usings.Concat(namespaceUsings)
            : namespaceUsings;
        return allUsings.Where(u => u.Alias != null);
    }
}