/// <summary>
        /// Let the add-on to prepare it's own data as a part of <see cref="RootMember"/> constructor.
        /// </summary>
        /// <remarks>
        /// The add-on checks all compilation files for the &lt;NuProp.xxx&gt; comments with source-only package metadata
        /// and builds the list of source-only packages and the indexes between the packages and code model members.
        /// </remarks>
        /// <param name="root">Code model root</param>
        /// <param name="builder">Code model buildel root</param>
        public void ProcessRootData(RootMember root, RootMemberBuilder builder)
        {
            ConsoleUtils.WriteInfo("SourceOnlyPackagesAddOn preparing data...");
            //Get source-only packages info
            var packages = new List <NuProps>();

            SourceOnlyPackages = packages;
            var sourceOnlyPackagesByMember = new Dictionary <Member, List <NuProps> >();

            SourceOnlyPackagesByMember = sourceOnlyPackagesByMember;
            var membersBySourceOnlyPackage = new Dictionary <NuProps, List <Member> >();

            MembersBySourceOnlyPackage = membersBySourceOnlyPackage;
            foreach (var compilationFile in builder.CompilationFiles)
            {
                var nuProps = new NuProps(compilationFile, builder.CompilationFiles);
                if (!nuProps.HasNuProps)
                {
                    continue;
                }
                packages.Add(nuProps);

                //add members to indexes
                foreach (var packageFile in nuProps.PackageFiles)
                {
                    if (!root.AllMembersBySourceFile.TryGetValue(packageFile, out var packageMembers))
                    {
                        continue;
                    }
                    foreach (var packageMember in packageMembers)
                    {
                        if (!sourceOnlyPackagesByMember.TryGetValue(packageMember, out var packagesForMember))
                        {
                            packagesForMember = new List <NuProps>();
                            sourceOnlyPackagesByMember.Add(packageMember, packagesForMember);
                        }
                        packagesForMember.Add(nuProps);

                        if (!membersBySourceOnlyPackage.TryGetValue(nuProps, out var membersForPackage))
                        {
                            membersForPackage = new List <Member>();
                            membersBySourceOnlyPackage.Add(nuProps, membersForPackage);
                        }
                        membersForPackage.Add(packageMember);
                    }
                }
            }
            ConsoleUtils.WriteInfo($"SourceOnlyPackagesAddOn finished preparing data - {packages.Count} source only packages found");
        }
Exemple #2
0
        /// <summary>
        /// Gets the <see cref="TypeRef"/> from the <see cref="ITypeSymbol"/>. When such TypeRef doesn't exist yet, it's created.
        /// </summary>
        /// <remarks>The list of existing type references is held by <see cref="RootMemberBuilder"/>.</remarks>
        /// <param name="symbol">Type symbol to get the TypeRef for</param>
        /// <param name="root">Root builder keeping the list of all type references</param>
        /// <returns><see cref="TypeRef"/> corresponding to the <paramref name="symbol"/></returns>
        public static TypeRef GetOrCreate(ITypeSymbol symbol, RootMemberBuilder root)
        {
            var ns              = symbol.ContainingNamespace?.ToDisplayString();
            var name            = symbol.ContainingType == null ? symbol.Name : GetOrCreate(symbol.ContainingType, root).Name + "." + symbol.Name;
            var namedTypeSymbol = symbol as INamedTypeSymbol;

            //Arrays
            TypeRef arrayElementType = null;

            if (symbol is IArrayTypeSymbol arrayTypeSymbol)
            {
                var element = arrayTypeSymbol.ElementType;
                var nameExt = $"[{(arrayTypeSymbol.Rank > 1 ? new string(',', arrayTypeSymbol.Rank - 1) : "")}]"; //multi array
                while (element is IArrayTypeSymbol elementArray)                                                  //array of arrays
                {
                    nameExt += $"[{(arrayTypeSymbol.Rank > 1 ? new string(',', arrayTypeSymbol.Rank - 1) : "")}]";
                    element  = elementArray.ElementType;
                }
                arrayElementType = GetOrCreate(element, root);
                ns   = arrayElementType.Namespace;
                name = arrayElementType.Name + nameExt;
            }

            //Tuples
            if (symbol.IsTupleType && namedTypeSymbol != null)
            {
                var tupleElements       = namedTypeSymbol.TupleElements;
                var tupleUnderlyingType = namedTypeSymbol.TupleUnderlyingType;
                var parts = new List <string>();
                for (var i = 0; i < tupleUnderlyingType.TypeArguments.Length; i++)
                {
                    var itemTypeName = GetOrCreate(tupleUnderlyingType.TypeArguments[i], root).ApplySpecialName();
                    var itemName     = tupleElements[i].Name;
                    parts.Add($"{itemTypeName} {itemName}");
                }
                name = $"({string.Join(", ", parts)})";
                ns   = "";
            }

            //Generics
            var     needsGenericDefinition = false;
            var     genericBaseName        = name;
            TypeRef genericDefinition      = null;

            if (namedTypeSymbol != null &&
                namedTypeSymbol.IsGenericType &&
                namedTypeSymbol.TypeParameters != null &&
                namedTypeSymbol.TypeParameters.Length > 0)
            {
                //generic, add types to name
                var paramListApplied = new List <string>();
                for (var i = 0; i < namedTypeSymbol.TypeParameters.Length; i++)
                {
                    var arg = namedTypeSymbol.TypeArguments[i];
                    switch (arg)
                    {
                    case ITypeParameterSymbol p:
                    {
                        //for base class A<T> is class B<T>:A<T> or B<U>:A<U> (rename)
                        paramListApplied.Add(p.Name);
                        if (arg.Name != namedTypeSymbol.TypeParameters[i].Name)
                        {
                            needsGenericDefinition = true;                                                             //rename
                        }
                        break;
                    }

                    case IArrayTypeSymbol argArray:
                    {
                        //for base class A<T> is class B<U>:A<U[]>or  class B:A<some type[]>
                        if (argArray.ElementType is ITypeParameterSymbol argArrayElementParam)
                        {
                            //for base class A<T> is class B<U>:A<U[]>
                            paramListApplied.Add(argArrayElementParam.Name + "[]");
                        }
                        else
                        {
                            //for base class A<T> is class B:A<some type[]> {
                            paramListApplied.Add(GetOrCreate(argArray.ElementType, root) + "[]");
                        }

                        needsGenericDefinition = true;
                        break;
                    }

                    default:
                        //for base class A<T> is class B<U>:A<U> or  class B:A<some type>
                        paramListApplied.Add(GetOrCreate(arg, root).ToString());
                        needsGenericDefinition = true;
                        break;
                    }
                }
                name += $"<{string.Join(",", paramListApplied)}>";
                if (needsGenericDefinition)
                {
                    genericDefinition = GetOrCreate(namedTypeSymbol.ConstructedFrom, root);
                }
            }
            //End of special cases

            //Try to get existing item
            var existingItem = root.AllTypeRefs.FirstOrDefault(tr => tr.Namespace == ns && tr.Name == name);

            if (existingItem != null)
            {
                return(existingItem);
            }

            //Create a new TypeRef
            var retVal = new TypeRef
            {
                Namespace        = ns ?? "",
                Name             = name,
                DocumentationId  = namedTypeSymbol?.GetDocumentationCommentId(),
                GenericBaseName  = genericBaseName,
                GenericArity     = namedTypeSymbol?.Arity ?? 0,
                EnumValueType    = ApplySpecialName(namedTypeSymbol?.EnumUnderlyingType?.ContainingNamespace.Name, namedTypeSymbol?.EnumUnderlyingType?.Name, false),
                ArrayElementType = arrayElementType
            };

            root.AllTypeRefs.Add(retVal);

            if (symbol.BaseType != null)
            {
                retVal.Base = GetOrCreate(symbol.BaseType, root);
            }
            if (needsGenericDefinition)
            {
                retVal.GenericDefinition = genericDefinition;
            }

            return(retVal);
        }