// 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); } }