// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Simplification; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.NetCore.CSharp.Analyzers.Runtime { public sealed partial class CSharpForwardCancellationTokenToInvocationsFixer { private class TypeNameVisitor : SymbolVisitor<TypeSyntax> { public static TypeSyntax GetTypeSyntaxForSymbol(INamespaceOrTypeSymbol symbol) { return symbol.Accept(new TypeNameVisitor())!.WithAdditionalAnnotations(Simplifier.Annotation); } public override TypeSyntax DefaultVisit(ISymbol symbol) => throw new NotImplementedException(); public override TypeSyntax VisitAlias(IAliasSymbol symbol) { return AddInformationTo(ToIdentifierName(symbol.Name)); } public override TypeSyntax VisitDynamicType(IDynamicTypeSymbol symbol) { return AddInformationTo(IdentifierName("dynamic")); } public override TypeSyntax VisitNamedType(INamedTypeSymbol symbol) { if (TryCreateNativeIntegerType(symbol, out var typeSyntax)) return typeSyntax; typeSyntax = CreateSimpleTypeSyntax(symbol); if (typeSyntax is not SimpleNameSyntax simpleNameSyntax) return typeSyntax; if (symbol.ContainingType is not null) { if (symbol.ContainingType.TypeKind != TypeKind.Submission) { var containingTypeSyntax = symbol.ContainingType.Accept(this); if (containingTypeSyntax is NameSyntax name) { typeSyntax = AddInformationTo( QualifiedName(name, simpleNameSyntax)); } else { typeSyntax = AddInformationTo(simpleNameSyntax); } } } else if (symbol.ContainingNamespace is not null) { if (symbol.ContainingNamespace.IsGlobalNamespace) { if (symbol.TypeKind != TypeKind.Error) { typeSyntax = AddGlobalAlias(simpleNameSyntax); } } else { var container = symbol.ContainingNamespace.Accept(this)!; typeSyntax = AddInformationTo(QualifiedName( (NameSyntax)container, simpleNameSyntax)); } } if (symbol.NullableAnnotation == NullableAnnotation.Annotated && !symbol.IsValueType) { typeSyntax = AddInformationTo(NullableType(typeSyntax)); } return typeSyntax; } public override TypeSyntax VisitNamespace(INamespaceSymbol symbol) { var syntax = AddInformationTo(ToIdentifierName(symbol.Name)); if (symbol.ContainingNamespace == null) { return syntax; } if (symbol.ContainingNamespace.IsGlobalNamespace) { return AddGlobalAlias(syntax); } else { var container = symbol.ContainingNamespace.Accept(this)!; return AddInformationTo(QualifiedName( (NameSyntax)container, syntax)); } } public override TypeSyntax VisitTypeParameter(ITypeParameterSymbol symbol) { TypeSyntax typeSyntax = AddInformationTo(ToIdentifierName(symbol.Name)); if (symbol.NullableAnnotation == NullableAnnotation.Annotated) typeSyntax = AddInformationTo(NullableType(typeSyntax)); return typeSyntax; } private TypeSyntax CreateSimpleTypeSyntax(INamedTypeSymbol symbol) { if (symbol.IsTupleType && symbol.TupleUnderlyingType != null && !symbol.Equals(symbol.TupleUnderlyingType)) { return CreateSimpleTypeSyntax(symbol.TupleUnderlyingType); } if (string.IsNullOrEmpty(symbol.Name) || symbol.IsAnonymousType) { return CreateSystemObject(); } if (symbol.TypeParameters.Length == 0) { if (symbol.TypeKind == TypeKind.Error && symbol.Name == "var") { return CreateSystemObject(); } return ToIdentifierName(symbol.Name); } var typeArguments = symbol.IsUnboundGenericType ? Enumerable.Repeat((TypeSyntax)OmittedTypeArgument(), symbol.TypeArguments.Length) : symbol.TypeArguments.Select(GetTypeSyntaxForSymbol); return GenericName( ToIdentifierToken(symbol.Name), TypeArgumentList(SeparatedList(typeArguments))); } private static QualifiedNameSyntax CreateSystemObject() { return QualifiedName( AliasQualifiedName( CreateGlobalIdentifier(), IdentifierName("System")), IdentifierName("Object")); } private static TTypeSyntax AddInformationTo<TTypeSyntax>(TTypeSyntax syntax) where TTypeSyntax : TypeSyntax { syntax = syntax.WithLeadingTrivia(ElasticMarker).WithTrailingTrivia(ElasticMarker); return syntax; } /// <summary> /// We always unilaterally add "global::" to all named types/namespaces. This /// will then be trimmed off if possible by the simplifier /// </summary> private static TypeSyntax AddGlobalAlias(SimpleNameSyntax syntax) { return AddInformationTo(AliasQualifiedName(CreateGlobalIdentifier(), syntax)); } private static IdentifierNameSyntax ToIdentifierName(string identifier) => IdentifierName(ToIdentifierToken(identifier)); private static IdentifierNameSyntax CreateGlobalIdentifier() => IdentifierName(Token(SyntaxKind.GlobalKeyword)); private static bool TryCreateNativeIntegerType(INamedTypeSymbol symbol, [NotNullWhen(true)] out TypeSyntax? syntax) { if (symbol.IsNativeIntegerType) { syntax = IdentifierName(symbol.SpecialType == SpecialType.System_IntPtr ? "nint" : "nuint"); return true; } syntax = null; return false; } private static SyntaxToken ToIdentifierToken(string identifier) { var escaped = EscapeIdentifier(identifier); if (escaped.Length == 0 || escaped[0] != '@') { return Identifier(escaped); } var unescaped = identifier.StartsWith("@", StringComparison.Ordinal) ? identifier[1..] : identifier; var token = Identifier( default, SyntaxKind.None, "@" + unescaped, unescaped, default); if (!identifier.StartsWith("@", StringComparison.Ordinal)) { token = token.WithAdditionalAnnotations(Simplifier.Annotation); } return token; } private static string EscapeIdentifier(string identifier) { var nullIndex = identifier.IndexOf('\0'); if (nullIndex >= 0) { identifier = identifier[..nullIndex]; } var needsEscaping = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None; return needsEscaping ? "@" + identifier : identifier; } } } }