File: Language\Components\TypeNameHelper.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// 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.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
internal static partial class TypeNameHelper
{
    private const string GlobalPrefix = "global::";
 
    private static readonly ImmutableHashSet<ReadOnlyMemory<char>> PredefinedTypeNames = new[]
    {
        "bool".AsMemory(),
        "int".AsMemory(),
        "string".AsMemory(),
        "float".AsMemory(),
        "double".AsMemory(),
        "decimal".AsMemory(),
        "byte".AsMemory(),
        "short".AsMemory(),
        "long".AsMemory(),
        "char".AsMemory(),
        "object".AsMemory(),
        "dynamic".AsMemory(),
        "uint".AsMemory(),
        "ushort".AsMemory(),
        "ulong".AsMemory(),
        "sbyte".AsMemory(),
        "nint".AsMemory(),
        "nuint".AsMemory(),
    }.ToImmutableHashSet(NameComparer.Instance);
 
    internal static string GetGloballyQualifiedNameIfNeeded(string typeName)
    {
        if (typeName.Length == 0)
        {
            return typeName;
        }
 
        if (typeName.StartsWith(GlobalPrefix, StringComparison.Ordinal))
        {
            return typeName;
        }
 
        // Mitigation for https://github.com/dotnet/razor-compiler/issues/332. When we add a reference to Roslyn
        // at this layer, we can do this property by using ParseTypeName and then rewriting the tree. For now, we
        // just skip prefixing tuples.
        if (typeName[0] == '(')
        {
            return typeName;
        }
 
        // Fast path, if the length doesn't fall within that of the
        // builtin c# types, then we can add global without further checks.
        if (typeName.Length is < 3 or > 7)
        {
            return GlobalPrefix + typeName;
        }
 
        if (PredefinedTypeNames.Contains(typeName.AsMemory()))
        {
            return typeName;
        }
 
        return GlobalPrefix + typeName;
    }
 
    public static void WriteGloballyQualifiedName(CodeWriter codeWriter, string typeName)
    {
        if (typeName == null)
        {
            throw new ArgumentNullException(nameof(typeName));
        }
 
        WriteGloballyQualifiedName(codeWriter, typeName.AsMemory());
    }
 
    internal static void WriteGloballyQualifiedName(CodeWriter codeWriter, ReadOnlyMemory<char> typeName)
    {
        WriteGlobalPrefixIfNeeded(codeWriter, typeName);
        codeWriter.Write(typeName);
    }
 
    /// <summary>
    /// Writes "global::" if the typename doesn't already start with it and isn't a predefined type.
    /// </summary>
    internal static void WriteGlobalPrefixIfNeeded(CodeWriter codeWriter, ReadOnlyMemory<char> typeName)
    {
        if (typeName.Length == 0)
        {
            return;
        }
 
        var typeNameSpan = typeName.Span;
 
        if (typeNameSpan.StartsWith(GlobalPrefix.AsSpan(), StringComparison.Ordinal))
        {
            return;
        }
 
        // Mitigation for https://github.com/dotnet/razor-compiler/issues/332. When we add a reference to Roslyn
        // at this layer, we can do this property by using ParseTypeName and then rewriting the tree. For now, we
        // just skip prefixing tuples.
        if (typeNameSpan[0] == '(')
        {
            return;
        }
 
        // Fast path, if the length doesn't fall within that of the
        // builtin c# types, then we can add global without further checks.
        if (typeNameSpan.Length < 3 || typeNameSpan.Length > 7)
        {
            codeWriter.Write(GlobalPrefix);
            return;
        }
 
        if (PredefinedTypeNames.Contains(typeName))
        {
            return;
        }
 
        codeWriter.Write(GlobalPrefix);
    }
 
    internal static ReadOnlyMemory<char> GetNonGenericTypeName(string typeName, out ReadOnlyMemory<char> genericTypeParameterList)
    {
        var memory = typeName.AsMemory();
        var index = memory.Span.IndexOf('<');
 
        genericTypeParameterList = index == -1
            ? default
            : memory[index..];
        return index == -1 ? memory : memory[..index];
    }
}