/// <summary> /// Prepares the <see cref="AssemblyMemberBuilder"/> from the <paramref name="symbol"/> /// and adds it to <see cref="RootMember.Assemblies"/> /// </summary> /// <param name="symbol">Source <see cref="IAssemblySymbol"/></param> /// <param name="root">Builder root</param> /// <param name="level">Hierarchy level (used to indent the console output)</param> private static void BuildAssembly(IAssemblySymbol symbol, RootMemberBuilder root, int level) { var a = new AssemblyMemberBuilder { Name = symbol.Name, NameBase = symbol.Name, Symbol = symbol, DocumentationId = symbol.GetDocumentationCommentId(), DocumentationXml = symbol.GetDocumentationCommentXml() }; root.Assemblies.Add(a); Console.WriteLine($"{new string(' ', level)} read as {a}"); // ReSharper disable once TailRecursiveCall Build(symbol.GlobalNamespace, root, a, null, null, level + 1); }
/// <summary> /// Walks through the Roslyn symbols hierarchy and prepares the member builders hierarchy into the given <paramref name="root"/> builder. /// </summary> /// <param name="symbol">Symbol to process</param> /// <param name="root">Root builder</param> /// <param name="assembly">Containing assembly builder</param> /// <param name="ns">Containing namespace builder</param> /// <param name="type">Containing type builder (used for nested types)</param> /// <param name="level">Hierarchy level (used to indent the console output)</param> private static void Build(ISymbol symbol, RootMemberBuilder root, AssemblyMemberBuilder assembly, NamespaceMemberBuilder ns, TypeMemberBuilder type, int level) { Console.WriteLine($"{new string(' ', level)}Checking the {symbol.Kind}: {symbol.Name}"); try { switch (symbol) { case IAssemblySymbol assemblySymbol: BuildAssembly(assemblySymbol, root, level); break; case INamespaceSymbol namespaceSymbol: BuildNamespace(namespaceSymbol, root, assembly, level); break; case INamedTypeSymbol typeSymbol: BuildType(typeSymbol, root, assembly, ns, type, level); break; case IFieldSymbol fieldSymbol: BuildField(fieldSymbol, root, type, level); break; case IPropertySymbol propertySymbol: BuildProperty(propertySymbol, root, type, level); break; case IMethodSymbol methodSymbol: BuildMethod(methodSymbol, root, type, level); break; case IEventSymbol eventSymbol: BuildEvent(eventSymbol, root, type, level); break; } } catch (Exception exception) { ConsoleUtils.WriteErr($"{new string(' ', level)}Exception '{exception.Message}' while processing {symbol.Kind}: {symbol.Name}"); throw; } }
/// <summary> /// Prepares the <see cref="TypeMemberBuilder"/> from the <paramref name="symbol"/> /// and adds it to <see cref="NamespaceMemberBuilder.Types"/> /// </summary> /// <param name="symbol">Source <see cref="INamedTypeSymbol"/></param> /// <param name="root">Builder root</param> /// <param name="assembly">Containing <see cref="AssemblyMemberBuilder"/></param> /// <param name="ns">Parent <see cref="NamespaceMemberBuilder"/></param> /// <param name="type">Parent <see cref="TypeMemberBuilder"/> for nested types</param> /// <param name="level">Hierarchy level (used to indent the console output)</param> // ReSharper disable once SuggestBaseTypeForParameter private static void BuildType(INamedTypeSymbol symbol, RootMemberBuilder root, AssemblyMemberBuilder assembly, NamespaceMemberBuilder ns, TypeMemberBuilder type, int level) { if (symbol.IsImplicitlyDeclared) { return; } if (symbol.GetAttributes().Any(a => a.AttributeClassString() == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")) { return; } if (symbol.Name.Contains("NuProps")) { } var t = new TypeMemberBuilder() { Name = type == null ? symbol.Name : $"{type.Name}.{symbol.Name}", Symbol = symbol, SourceFiles = symbol.DeclaringSyntaxReferences.Select(dsr => dsr.SyntaxTree.FilePath).ToList(), DocumentationId = symbol.GetDocumentationCommentId(), DocumentationXml = symbol.GetDocumentationCommentXml(), Documentation = Documentation.Read(symbol.GetDocumentationCommentXml()), TypeKind = symbol.TypeKind.ToTypeKindEnum(), TypeRef = TypeRef.GetOrCreate(symbol, root), Modifier = ModifierEnumExtensions.Modifier(symbol.DeclaredAccessibility), IsAbstract = symbol.IsAbstract, IsExtern = symbol.IsExtern, IsSealed = symbol.IsSealed, IsStatic = symbol.IsStatic, IsNew = symbol.GetIsNew(), IsGeneric = symbol.IsGenericType && symbol.TypeParameters != null && symbol.TypeParameters.Length > 0 }; t.NameBase = t.Name; //Get the implemented interfaces var typeInterfaces = symbol.Interfaces; if (typeInterfaces != null && typeInterfaces.Length > 0) { //directly implemented t.InterfacesTypeRefs = typeInterfaces.Select(i => TypeRef.GetOrCreate(i, root)).ToList(); } var typeAllInterfaces = symbol.AllInterfaces; if (typeAllInterfaces != null && typeAllInterfaces.Length > 0) { //includes the inherited t.AllInterfacesTypeRefs = typeAllInterfaces.Select(i => TypeRef.GetOrCreate(i, root)).ToList(); foreach (var implementedInterface in typeAllInterfaces) { var interfaceMembers = implementedInterface.GetMembers(); if (interfaceMembers == null) { continue; } foreach (var interfaceMember in interfaceMembers.Where( im => im.Kind == SymbolKind.Event || im.Kind == SymbolKind.Property || im.Kind == SymbolKind.Method)) { var implementationMember = symbol.FindImplementationForInterfaceMember(interfaceMember); if (implementationMember == null) { continue; } t.InterfaceImplementationsByInterfaceMember.Add(interfaceMember, implementationMember); if (!t.InterfaceMembersByInterfaceImplementation.TryGetValue(implementationMember, out var interfaceMemberList)) { interfaceMemberList = new List <ISymbol>(); t.InterfaceMembersByInterfaceImplementation.Add(implementationMember, interfaceMemberList); } ((List <ISymbol>)interfaceMemberList).Add(interfaceMember); } } } //Get the attributes t.SetAttributes(root); if (t.IsGeneric) { //Process the type parameters t.TypeParameters = GetTypeParameters(symbol.TypeParameters, root); t.Name += $"<{string.Join(",", t.TypeParameters.Select(tp => tp.Name))}>"; // add types to name } if (t.TypeKind == TypeKindEnum.Delegate) { //Process delegate - delegates only have parameters and return value t.DelegateReturnType = TypeRef.GetOrCreate(symbol.DelegateInvokeMethod.ReturnType, root); t.DelegateParameters = GetMethodParameters(symbol.DelegateInvokeMethod.Parameters, root); } ns.Types.Add(t); Console.WriteLine($"{new string(' ', level)} read as {t}"); foreach (var subType in symbol.GetMembers()) { if (!subType.IsImplicitlyDeclared) { Build(subType, root, assembly, ns, t, level + 1); } } }
/// <summary> /// Prepares the <see cref="NamespaceMemberBuilder"/> from the <paramref name="symbol"/> /// and adds it to <see cref="AssemblyMemberBuilder.Namespaces"/> /// </summary> /// <param name="symbol">Source <see cref="INamespaceSymbol"/></param> /// <param name="root">Builder root</param> /// <param name="assembly">Parent <see cref="AssemblyMemberBuilder"/></param> /// <param name="level">Hierarchy level (used to indent the console output)</param> private static void BuildNamespace(INamespaceSymbol symbol, RootMemberBuilder root, AssemblyMemberBuilder assembly, int level) { var children = symbol.GetMembers().ToArray(); var childNamespaces = children.OfType <INamespaceSymbol>().ToArray(); var childTypes = children.OfType <ITypeSymbol>().ToArray(); NamespaceMemberBuilder n = null; if (childTypes.Length > 0) //check if not "empty" namespace resp. part of "dot syntax" (when there is a ns a.b.c.d and "d" is the only one containing the types, the a,b and c are in symbols hierarchy as "empty" ns { n = new NamespaceMemberBuilder() { Name = SymbolDisplay.ToDisplayString(symbol), NameBase = SymbolDisplay.ToDisplayString(symbol), Symbol = symbol, DocumentationId = symbol.GetDocumentationCommentId(), }; //ns documentation - internal class NamespaceDoc { } var docClass = symbol.GetMembers("NamespaceDoc").OfType <INamedTypeSymbol>().FirstOrDefault(); if (docClass != null) { n.DocumentationXml = docClass.GetDocumentationCommentXml(); n.Documentation = Documentation.Read(n.DocumentationXml); } assembly.Namespaces.Add(n); Console.WriteLine($"{new string(' ', level)} read as {n}"); foreach (var memberSymbol in childTypes) { Build(memberSymbol, root, assembly, n, null, level + 1); } } foreach (var memberSymbol in childNamespaces) { Build(memberSymbol, root, assembly, n, null, level + 1); } }