File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\Extensions\ITypeSymbolExtensions.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.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);
    }
}