C# find class file path for any class

I have looked all over for this, but to no avail.

What I want to do is the following: determine the path of the class file in which to find a specified C# class (non-monobehaviour/ScriptableObject). I already know how to find monobehaviour scripts.

I’ve looked on the C# boards but it is mostly concerned with how to find a class library (.dll) which contains a certain class. It’s probably not a situation that happens outside of this environment.

I realized this is more a .net issue than a Unity issue and goes beyond the scope of Unity Answers. Relisted the question here…

Here in 2023. I offer my code as another solution to this problem. You will require a few code analysis Dlls, but so far it works great and allowed me to open C# files directly from a right-click menu. I have something like 80-100 .cs files in my project and no SSD on the machine I’m using. Generally runs in about 1/4 of a second, but you could probably optimize if you want. I’m not sure what it does with generics, I haven’t tested that.

/// <summary>
/// Utility class for code analysis.
/// </summary>
public static class CodeAnalysis
{
    private static Dictionary<string, string> ClassNameToFileMapping = new Dictionary<string, string>();

    /// <summary>
    /// When this class is first loaded, we'll compile a list of all classes.
    /// </summary>
    static CodeAnalysis()
    {
        string[] cSharpFiles = Directory.GetFiles(Application.dataPath, "*.cs", SearchOption.AllDirectories);
        foreach (string cSharpFile in cSharpFiles)
        {
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(cSharpFile));
            var root = syntaxTree.GetCompilationUnitRoot();
            foreach (SyntaxNode node in root.DescendantNodes())
            {
                if (node is TypeDeclarationSyntax syntaxNode)
                {
                    string fullname = syntaxNode.Identifier.ValueText;
                    IEnumerable<SyntaxNode> parentsAndSelf = syntaxNode.AncestorsAndSelf().Where(node => node is TypeDeclarationSyntax || node is NamespaceDeclarationSyntax);
                    StringBuilder stringBuilder = new StringBuilder();

                    bool typeEncountered = false;
                    bool anyEncountered = false;
                    foreach (SyntaxNode nameBuilderNode in parentsAndSelf.Reverse())
                    {
                        switch (nameBuilderNode)
                        {
                            case TypeDeclarationSyntax typeSyntax:
                                if (typeEncountered) stringBuilder.Append("+");
                                else if (anyEncountered) stringBuilder.Append(".");

                                // Appending the type name
                                stringBuilder.Append(typeSyntax.Identifier.ValueText);

                                typeEncountered = true;
                                break;
                            case NamespaceDeclarationSyntax namespaceSyntax:
                                if (anyEncountered) stringBuilder.Append(".");

                                // appending the namespace name
                                stringBuilder.Append(namespaceSyntax.Name.GetText());

                                break;
                            default:
                                break;
                        }

                        anyEncountered = true;
                    }

                    // The carriage return can sometimes end up here. Need to remove it.
                    string className = stringBuilder.ToString().Replace("

", “”);

                    // We have to do this check for partial classes.
                    if (!ClassNameToFileMapping.ContainsKey(className))
                    {
                        ClassNameToFileMapping.Add(className, cSharpFile);
                    }
                }
            }
        }
    }

    /// <summary>
    /// Get a file for a type.
    /// </summary>
    public static string GetFileForType(Type type)
    {
        return ClassNameToFileMapping[type.FullName];
    }

    public static string GetAssetPathForType(Type type)
    {
        string filePath = GetFileForType(type);
        string assetPath = "Assets" + filePath.Substring(Application.dataPath.Length);
        return assetPath;
    }
}