| File: TypeParser.cs | Web Access |
| Project: src\arcade\src\Microsoft.DotNet.GenFacades\Microsoft.DotNet.GenFacades.csproj (Microsoft.DotNet.GenFacades) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Microsoft.DotNet.GenFacades { internal class TypeParser { public static HashSet<string> GetAllPublicTypes(IEnumerable<string> files, IEnumerable<string> constants, string langVersion) { HashSet<string> types = new HashSet<string>(); LanguageVersion languageVersion = LanguageVersion.Default; if (!string.IsNullOrEmpty(langVersion) && !LanguageVersionFacts.TryParse(langVersion, out languageVersion)) { throw new ArgumentException($"Invalid C# language version value '{langVersion}'", nameof(langVersion)); } var syntaxTreeCollection = GetSourceTrees(files, constants, languageVersion); foreach (var tree in syntaxTreeCollection) { AddTypesFromTypeForwards(tree, types); AddBaseTypes(tree, types); AddTypesFromDelegates(tree, types); } return types; } private static void AddTypesFromTypeForwards(SyntaxTree tree, HashSet<string> types) { CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); foreach (var attribute in root.AttributeLists) { foreach (var item in attribute.Attributes) { string attributeString = item.ToFullString(); if (attributeString.Contains("TypeForwardedTo")) { var typeNameExpression = (TypeOfExpressionSyntax)item.ArgumentList.Arguments[0].Expression; string typeName = typeNameExpression.Type.ToFullString(); types.Add(typeName); } } } } private static void AddBaseTypes(SyntaxTree tree, HashSet<string> types) { CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); var allPublicTypes = root.DescendantNodes().OfType<BaseTypeDeclarationSyntax>() .Where(t => HasPublicModifier(t)); foreach (var item in allPublicTypes) { string fullyQualifiedName = GetFullyQualifiedName(item); if (!types.Contains(fullyQualifiedName)) types.Add(fullyQualifiedName); } } private static void AddTypesFromDelegates(SyntaxTree tree, HashSet<string> types) { CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); var allPublicTypes = root.DescendantNodes().OfType<DelegateDeclarationSyntax>() .Where(t => HasPublicModifier(t)); foreach (var item in allPublicTypes) { string fullyQualifiedName; if (item.Parent is NamespaceDeclarationSyntax parent) { fullyQualifiedName = parent.Name.ToFullString().Trim() + "." + GetDelegateTypeName(item); } else { fullyQualifiedName = GetFullyQualifiedName((BaseTypeDeclarationSyntax)item.Parent, item.Identifier.ValueText.Trim()); } if (!types.Contains(fullyQualifiedName)) types.Add(fullyQualifiedName); } } private static string GetFullyQualifiedName(BaseTypeDeclarationSyntax node, string nested = "") { string typeName = GetBaseTypeName(node); if (node.Parent is NamespaceDeclarationSyntax parent) { string withoutNested = GetNamespaceName(parent) + "." + typeName; return string.IsNullOrEmpty(nested) ? withoutNested : withoutNested + "." + nested; } return GetFullyQualifiedName((BaseTypeDeclarationSyntax)node.Parent, string.IsNullOrEmpty(nested) ? typeName : typeName + "." + nested); } // BaseType here refers to classes, structs, interfaces and enums. private static string GetBaseTypeName(BaseTypeDeclarationSyntax type) { string typeName = type.Identifier.ValueText; if (type is TypeDeclarationSyntax actualType) { return GetTypeNameWithTypeParameter(actualType.TypeParameterList, typeName); } return typeName; } private static string GetDelegateTypeName(DelegateDeclarationSyntax type) { string typeName = type.Identifier.ValueText; return GetTypeNameWithTypeParameter(type.TypeParameterList, typeName); } private static string GetTypeNameWithTypeParameter(TypeParameterListSyntax typeParameterList, string identifier) { return typeParameterList != null ? identifier + "`" + typeParameterList.Parameters.Count : identifier; } private static string GetNamespaceName(NamespaceDeclarationSyntax namespaceSyntax) { return namespaceSyntax.Name.ToFullString().Trim(); } private static IEnumerable<SyntaxTree> GetSourceTrees(IEnumerable<string> sourceFiles, IEnumerable<string> constants, LanguageVersion languageVersion) { CSharpParseOptions options = new CSharpParseOptions(languageVersion: languageVersion, preprocessorSymbols: constants); List<SyntaxTree> result = new List<SyntaxTree>(); foreach (string sourceFile in sourceFiles) { if (string.IsNullOrEmpty(sourceFile)) { continue; } if (!File.Exists(sourceFile)) { throw new FileNotFoundException($"File {sourceFile} was not found."); } SyntaxTree tree = CSharpSyntaxTree.ParseText(File.ReadAllText(sourceFile), options); result.Add(tree); } return result; } private static bool HasPublicModifier(BaseTypeDeclarationSyntax token) { if (token.Parent == null || token.Parent is NamespaceDeclarationSyntax) return HasPublicModifier(token.Modifiers); return HasPublicModifier(token.Modifiers) && HasPublicModifier((BaseTypeDeclarationSyntax)(token.Parent)); } private static bool HasPublicModifier(DelegateDeclarationSyntax token) { if (token.Parent == null || token.Parent is NamespaceDeclarationSyntax) return HasPublicModifier(token.Modifiers); return HasPublicModifier(token.Modifiers) && HasPublicModifier((BaseTypeDeclarationSyntax)(token.Parent)); } private static bool HasPublicModifier(SyntaxTokenList modifiers) { foreach (SyntaxToken modifier in modifiers) { if (modifier.Text == "public") { return true; } } return false; } } }