Example #1
0
        private void PortMissingCommentsForMember(DocsMember dMemberToUpdate)
        {
            string docId = dMemberToUpdate.DocIdEscaped;
            IntelliSenseXmlMember?tsMemberToPort = IntelliSenseXmlComments.Members.FirstOrDefault(x => x.Name == docId);

            TryGetEIIMember(dMemberToUpdate, out DocsMember? interfacedMember);

            if (tsMemberToPort != null || interfacedMember != null)
            {
                TryPortMissingSummaryForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingRemarksForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember, Config.SkipInterfaceRemarks);
                TryPortMissingParamsForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingTypeParamsForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingExceptionsForMember(dMemberToUpdate, tsMemberToPort);

                // Properties sometimes don't have a <value> but have a <returns>
                if (dMemberToUpdate.MemberType == "Property")
                {
                    TryPortMissingPropertyForMember(dMemberToUpdate, tsMemberToPort, interfacedMember);
                }
                else if (dMemberToUpdate.MemberType == "Method")
                {
                    TryPortMissingMethodForMember(dMemberToUpdate, tsMemberToPort, interfacedMember);
                }

                if (dMemberToUpdate.Changed)
                {
                    ModifiedAPIs.AddIfNotExists(dMemberToUpdate.DocId);
                    ModifiedFiles.AddIfNotExists(dMemberToUpdate.FilePath);
                }
            }
        }
Example #2
0
        private void TryPortMissingMethodForMember(DocsMember dMemberToUpdate, TripleSlashMember?tsMemberToPort, DocsMember?interfacedMember)
        {
            if (IsEmpty(dMemberToUpdate.Returns))
            {
                string name  = string.Empty;
                string value = string.Empty;
                bool   isEII = false;

                // Bug: Sometimes a void return value shows up as not documented, skip those
                if (dMemberToUpdate.ReturnType == "System.Void")
                {
                    ProblematicAPIs.AddIfNotExists($"Unexpected System.Void return value in Method=[{dMemberToUpdate.DocId}]");
                }
                else if (tsMemberToPort != null && !IsEmpty(tsMemberToPort.Returns))
                {
                    name  = tsMemberToPort.Name;
                    value = tsMemberToPort.Returns;
                }
                else if (interfacedMember != null && !IsEmpty(interfacedMember.Returns))
                {
                    name  = interfacedMember.MemberName;
                    value = interfacedMember.Returns;
                    isEII = true;
                }

                if (!IsEmpty(value))
                {
                    dMemberToUpdate.Returns = value;
                    string message = $"METHOD {GetIsEII(isEII)} RETURNS";
                    PrintModifiedMember(message, dMemberToUpdate.FilePath, dMemberToUpdate.DocId);
                    TotalModifiedIndividualElements++;
                }
            }
        }
Example #3
0
        private void TryPortMissingRemarksForAPI(IDocsAPI dApiToUpdate, IntelliSenseXmlMember?tsMemberToPort, DocsMember?interfacedMember, bool skipInterfaceRemarks)
        {
            if (dApiToUpdate.Kind == APIKind.Type && !Config.PortTypeRemarks ||
                dApiToUpdate.Kind == APIKind.Member && !Config.PortMemberRemarks)
            {
                return;
            }

            if (dApiToUpdate.Remarks.IsDocsEmpty())
            {
                bool   isEII = false;
                string name  = string.Empty;
                string value = string.Empty;

                // Try to port IntelliSense xml comments
                if (tsMemberToPort != null && !tsMemberToPort.Remarks.IsDocsEmpty())
                {
                    dApiToUpdate.Remarks = tsMemberToPort.Remarks;
                    name  = tsMemberToPort.Name;
                    value = tsMemberToPort.Remarks;
                }
                // or try to find if it implements a documented interface
                // which only happens in docs members (types have a null interfacedMember passed)
                else if (interfacedMember != null && !interfacedMember.Remarks.IsDocsEmpty())
                {
                    DocsMember memberToUpdate = (DocsMember)dApiToUpdate;

                    // Only attempt to port if the member name is the same as the interfaced member docid without prefix
                    if (memberToUpdate.MemberName == interfacedMember.DocId[2..])
                    {
                        string dMemberToUpdateTypeDocIdNoPrefix  = memberToUpdate.ParentType.DocId[2..];
Example #4
0
        private void TryPortMissingTypeParamsForMember(DocsMember dMemberToUpdate, TripleSlashMember?tsMemberToPort, DocsMember?interfacedMember)
        {
            if (tsMemberToPort != null)
            {
                foreach (TripleSlashTypeParam tsTypeParam in tsMemberToPort.TypeParams)
                {
                    bool   isEII = false;
                    string name  = string.Empty;
                    string value = string.Empty;

                    DocsTypeParam dTypeParam = dMemberToUpdate.TypeParams.FirstOrDefault(x => x.Name == tsTypeParam.Name);

                    bool created = false;
                    if (dTypeParam == null)
                    {
                        ProblematicAPIs.AddIfNotExists($"TypeParam=[{tsTypeParam.Name}] in Member=[{dMemberToUpdate.DocId}]");
                        dTypeParam = dMemberToUpdate.AddTypeParam(tsTypeParam.Name, XmlHelper.GetNodesInPlainText(tsTypeParam.XETypeParam));
                        created    = true;
                    }

                    // But it can still be empty, try to retrieve it
                    if (IsEmpty(dTypeParam.Value))
                    {
                        // try to port triple slash comments
                        if (!IsEmpty(tsTypeParam.Value))
                        {
                            name  = tsTypeParam.Name;
                            value = tsTypeParam.Value;
                        }
                        // or try to find if it implements a documented interface
                        else if (interfacedMember != null)
                        {
                            DocsTypeParam interfacedTypeParam = interfacedMember.TypeParams.FirstOrDefault(x => x.Name == dTypeParam.Name);
                            if (interfacedTypeParam != null)
                            {
                                name  = interfacedTypeParam.Name;
                                value = interfacedTypeParam.Value;
                                isEII = true;
                            }
                        }
                    }

                    if (!IsEmpty(value))
                    {
                        dTypeParam.Value = value;
                        string message = $"MEMBER {GetIsEII(isEII)} TYPEPARAM ({GetIsCreated(created)})";
                        PrintModifiedMember(message, dTypeParam.ParentAPI.FilePath, dMemberToUpdate.DocId);
                        TotalModifiedIndividualElements++;
                    }
                }
            }
        }
Example #5
0
        private void TryPortMissingPropertyForMember(DocsMember dMemberToUpdate, TripleSlashMember?tsMemberToPort, DocsMember?interfacedMember)
        {
            if (IsEmpty(dMemberToUpdate.Value))
            {
                string name  = string.Empty;
                string value = string.Empty;
                bool   isEII = false;

                // Issue: sometimes properties have their TS string in Value, sometimes in Returns
                if (tsMemberToPort != null)
                {
                    name = tsMemberToPort.Name;
                    if (!IsEmpty(tsMemberToPort.Value))
                    {
                        value = tsMemberToPort.Value;
                    }
                    else if (!IsEmpty(tsMemberToPort.Returns))
                    {
                        value = tsMemberToPort.Returns;
                    }
                }
                // or try to find if it implements a documented interface
                else if (interfacedMember != null)
                {
                    name = interfacedMember.MemberName;
                    if (!IsEmpty(interfacedMember.Value))
                    {
                        value = interfacedMember.Value;
                    }
                    else if (!IsEmpty(interfacedMember.Returns))
                    {
                        value = interfacedMember.Returns;
                    }
                    if (!string.IsNullOrEmpty(value))
                    {
                        isEII = true;
                    }
                }

                if (!IsEmpty(value))
                {
                    dMemberToUpdate.Value = value;
                    string message = $"MEMBER {GetIsEII(isEII)} PROPERTY";
                    PrintModifiedMember(message, dMemberToUpdate.FilePath, dMemberToUpdate.DocId);
                    TotalModifiedIndividualElements++;
                }
            }
        }
Example #6
0
        private bool IsTypeAllowed(DocsAPI api)
        {
            // All types are allowed
            if (Config.ExcludedTypes.Count() == 0 &&
                Config.IncludedTypes.Count() == 0)
            {
                return(true);
            }

            string typeName;
            string typeFullName;

            if (api is DocsType)
            {
                DocsType type = (DocsType)api;
                typeName     = type.Name;
                typeFullName = type.FullName;
            }
            else if (api is DocsMember)
            {
                DocsMember member = (DocsMember)api;
                typeName     = member.ParentType.Name;
                typeFullName = member.ParentType.FullName;
            }
            else
            {
                throw new InvalidCastException();
            }

            if (Config.ExcludedTypes.Count() > 0)
            {
                if (Config.ExcludedTypes.Contains(typeName) || Config.ExcludedTypes.Contains(typeFullName))
                {
                    return(false);
                }
            }
            if (Config.IncludedTypes.Count() > 0)
            {
                if (Config.IncludedTypes.Contains(typeName) || Config.IncludedTypes.Contains(typeFullName))
                {
                    return(true);
                }
            }

            return(false);
        }
Example #7
0
        private void TryPortMissingExceptionsForMember(DocsMember dMemberToUpdate, TripleSlashMember?tsMemberToPort)
        {
            if (Config.SkipExceptions)
            {
                return;
            }

            if (tsMemberToPort != null)
            {
                // Exceptions are a special case: If a new one is found in code, but does not exist in docs, the whole element needs to be added
                foreach (TripleSlashException tsException in tsMemberToPort.Exceptions)
                {
                    DocsException dException = dMemberToUpdate.Exceptions.FirstOrDefault(x => x.Cref == tsException.Cref);
                    bool          created    = false;

                    // First time adding the cref
                    if (dException == null)
                    {
                        AddedExceptions.AddIfNotExists($"Exception=[{tsException.Cref}] in Member=[{dMemberToUpdate.DocId}]");
                        dException = dMemberToUpdate.AddException(tsException.Cref, XmlHelper.GetNodesInPlainText(tsException.XEException));
                        created    = true;
                    }
                    // If cref exists, check if the text has already been appended
                    else
                    {
                        XElement formattedException = tsException.XEException;
                        string   value = XmlHelper.GetNodesInPlainText(formattedException);
                        if (!dException.Value.Contains(value))
                        {
                            AddedExceptions.AddIfNotExists($"Exception=[{tsException.Cref}] in Member=[{dMemberToUpdate.DocId}]");
                            dException.AppendException(value);
                            created = true;
                        }
                    }

                    if (created || (!IsEmpty(tsException.Value) && IsEmpty(dException.Value)))
                    {
                        string message = string.Format("EXCEPTION ({0})", created ? "CREATED" : "MODIFIED");
                        PrintModifiedMember(message, dException.ParentAPI.FilePath, dException.Cref);

                        TotalModifiedIndividualElements++;
                    }
                }
            }
        }
Example #8
0
        private void PortMissingCommentsForMember(DocsMember dMemberToUpdate)
        {
            if (!CanAnalyzeAPI(dMemberToUpdate))
            {
                return;
            }

            TripleSlashMember tsMemberToPort = TripleSlashComments.Members.FirstOrDefault(x => x.Name == dMemberToUpdate.DocIdEscaped);

            TryGetEIIMember(dMemberToUpdate, out DocsMember? interfacedMember);

            if (tsMemberToPort != null || interfacedMember != null)
            {
                TryPortMissingSummaryForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingRemarksForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingParamsForAPI(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingTypeParamsForMember(dMemberToUpdate, tsMemberToPort, interfacedMember);
                TryPortMissingExceptionsForMember(dMemberToUpdate, tsMemberToPort);

                // Properties sometimes don't have a <value> but have a <returns>
                if (dMemberToUpdate.MemberType == "Property")
                {
                    TryPortMissingPropertyForMember(dMemberToUpdate, tsMemberToPort, interfacedMember);
                }
                else if (dMemberToUpdate.MemberType == "Method")
                {
                    TryPortMissingMethodForMember(dMemberToUpdate, tsMemberToPort, interfacedMember);
                }

                if (dMemberToUpdate.Changed)
                {
                    ModifiedAPIs.AddIfNotExists(dMemberToUpdate.DocId);
                    ModifiedFiles.AddIfNotExists(dMemberToUpdate.FilePath);
                }
            }
        }
Example #9
0
        private void TryPortMissingRemarksForAPI(IDocsAPI dApiToUpdate, TripleSlashMember?tsMemberToPort, DocsMember?interfacedMember)
        {
            if (Config.SkipRemarks)
            {
                return;
            }

            if (IsEmpty(dApiToUpdate.Remarks))
            {
                bool   isEII = false;
                string name  = string.Empty;
                string value = string.Empty;

                // Try to port triple slash comments
                if (tsMemberToPort != null && !IsEmpty(tsMemberToPort.Remarks))
                {
                    dApiToUpdate.Remarks = tsMemberToPort.Remarks;
                    name  = tsMemberToPort.Name;
                    value = tsMemberToPort.Remarks;
                }
                // or try to find if it implements a documented interface
                else if (interfacedMember != null && !IsEmpty(interfacedMember.Remarks))
                {
                    string eiiMessage = string.Empty;

                    DocsMember memberToUpdate = (DocsMember)dApiToUpdate;

                    // Special text for EIIs in Remarks
                    if (memberToUpdate.MemberName == interfacedMember.DocId.Substring(2))
                    {
                        string dMemberToUpdateTypeDocIdNoPrefix  = memberToUpdate.ParentType.DocId.Substring(2);
                        string interfacedMemberTypeDocIdNoPrefix = interfacedMember.ParentType.DocId.Substring(2);

                        eiiMessage = $"This member is an explicit interface member implementation. It can be used only when the <xref:{dMemberToUpdateTypeDocIdNoPrefix}> instance is cast to an <xref:{interfacedMemberTypeDocIdNoPrefix}> interface.{Environment.NewLine + Environment.NewLine}";
                    }

                    string original = string.Empty;
                    if (!interfacedMember.Remarks.Contains(Configuration.ToBeAdded))
                    {
                        original = interfacedMember.Remarks
                                   .CleanRemarksText("##Remarks")
                                   .CleanRemarksText("## Remarks")
                                   .CleanRemarksText("<![CDATA[")
                                   .CleanRemarksText("]]>");
                    }
                    dApiToUpdate.Remarks = eiiMessage + original;
                    name  = interfacedMember.MemberName;
                    value = interfacedMember.Remarks;

                    isEII = true;
                }

                if (!IsEmpty(value))
                {
                    // Any member can have an empty remark
                    string message = $"{dApiToUpdate.Prefix} {GetIsEII(isEII)} REMARKS";
                    PrintModifiedMember(message, dApiToUpdate.FilePath, dApiToUpdate.DocId);
                    TotalModifiedIndividualElements++;
                }
            }
        }
Example #10
0
        /// <summary>
        /// If a Param is found in the DocsMember that did not exist in the Triple Slash member, it's possible the param was unexpectedly saved in the triple slash comments with a different name, so the user gets prompted to look for it.
        /// </summary>
        /// <param name="tsParam">The problematic triple slash param object.</param>
        /// <param name="dMember">The docs member where the param lives.</param>
        /// <param name="dParam">The docs param that was found to not match the triple slash param.</param>
        /// <returns></returns>
        private static bool TryPromptParam(TripleSlashParam tsParam, DocsMember dMember, out DocsParam dParam)
        {
            dParam = null;
            bool created = false;

            int option = -1;

            while (option == -1)
            {
                Log.Error("Problem in member {0} in file {1}!", dMember.DocId, dMember.FilePath);
                Log.Warning("The param from triple slash called '{0}' probably exists in code, but the name was not found in Docs. What would you like to do?", tsParam.Name);
                Log.Warning("    1 - Type the correct name as it shows up in Docs.");
                Log.Warning("    2 - Add the newly detected param to the Docs file (not recommended).");
                Log.Warning("        Note: Whatever your choice, make sure to double check the affected Docs file after the tool finishes executing.");
                Log.Info(false, "Your answer [1,2]: ");

                if (!int.TryParse(Console.ReadLine(), out option))
                {
                    Log.Error("Invalid selection. Try again.");
                    option = -1;
                }
                else
                {
                    switch (option)
                    {
                    case 1:
                    {
                        string newName = string.Empty;
                        while (string.IsNullOrWhiteSpace(newName))
                        {
                            Log.Info(false, "Type the new name: ");
                            newName = Console.ReadLine().Trim();
                            if (string.IsNullOrWhiteSpace(newName))
                            {
                                Log.Error("Invalid selection. Try again.");
                            }
                            else if (newName == tsParam.Name)
                            {
                                Log.Error("You specified the same name. Try again.");
                                newName = string.Empty;
                            }
                            else
                            {
                                dParam = dMember.Params.FirstOrDefault(x => x.Name == newName);
                                if (dParam == null)
                                {
                                    Log.Error("Could not find the param with the selected name. Try again.");
                                    newName = string.Empty;
                                }
                                else
                                {
                                    Log.Success("Found the param with the selected name!");
                                }
                            }
                        }
                        break;
                    }

                    case 2:
                    {
                        dParam  = dMember.SaveParam(tsParam.XEParam);
                        created = true;
                        break;
                    }

                    default:
                    {
                        Log.Error("Invalid selection. Try again.");
                        option = -1;
                        break;
                    }
                    }
                }
            }

            return(created);
        }
Example #11
0
        private static bool TryPortMissingCommentsForMember(TripleSlashMember tsMember, DocsMember dMember)
        {
            bool modified = false;

            if (!IsEmpty(tsMember.Summary) && IsEmpty(dMember.Summary))
            {
                // Any member can have an empty summary
                PrintModifiedMember("MEMBER SUMMARY", dMember.FilePath, tsMember.Name, dMember.DocId, tsMember.Summary, dMember.Summary);

                dMember.Summary = tsMember.Summary;
                TotalModifiedIndividualElements++;
                modified = true;
            }

            if (!IsEmpty(tsMember.Remarks) && IsEmpty(dMember.Remarks))
            {
                // Any member can have an empty remark
                PrintModifiedMember("MEMBER REMARKS", dMember.FilePath, tsMember.Name, dMember.DocId, tsMember.Remarks, dMember.Remarks);

                dMember.Remarks = tsMember.Remarks;
                TotalModifiedIndividualElements++;
                modified = true;
            }

            // Properties and method returns save their values in different locations
            if (dMember.MemberType == "Property")
            {
                if (!IsEmpty(tsMember.Returns) && IsEmpty(dMember.Value))
                {
                    PrintModifiedMember("PROPERTY", dMember.FilePath, tsMember.Name, dMember.DocId, tsMember.Returns, dMember.Value);

                    dMember.Value = tsMember.Returns;
                    TotalModifiedIndividualElements++;
                    modified = true;
                }
            }
            else if (dMember.MemberType == "Method")
            {
                if (!IsEmpty(tsMember.Returns) && IsEmpty(dMember.Returns))
                {
                    if (tsMember.Returns != null && dMember.ReturnType == "System.Void")
                    {
                        ProblematicAPIs.AddIfNotExists($"Returns=[{tsMember.Returns}] in Method=[{dMember.DocId}]");
                    }
                    else
                    {
                        PrintModifiedMember("METHOD RETURN", dMember.FilePath, tsMember.Name, dMember.DocId, tsMember.Returns, dMember.Returns);

                        dMember.Returns = tsMember.Returns;
                        TotalModifiedIndividualElements++;
                        modified = true;
                    }
                }
            }

            // Triple slash params may cause errors if they are missing in the code side
            foreach (TripleSlashParam tsParam in tsMember.Params)
            {
                DocsParam dParam  = dMember.Params.FirstOrDefault(x => x.Name == tsParam.Name);
                bool      created = false;

                if (dParam == null)
                {
                    ProblematicAPIs.AddIfNotExists($"Param=[{tsParam.Name}] in Member DocId=[{dMember.DocId}]");

                    created = TryPromptParam(tsParam, dMember, out dParam);
                }

                if (created || (!IsEmpty(tsParam.Value) && IsEmpty(dParam.Value)))
                {
                    PrintModifiedMember(string.Format("PARAM ({0})", created ? "CREATED" : "MODIFIED"), dParam.FilePath, tsParam.Name, dParam.Name, tsParam.Value, dParam.Value);

                    if (!created)
                    {
                        dParam.Value = tsParam.Value;
                    }
                    TotalModifiedIndividualElements++;
                    modified = true;
                }
            }

            // Exceptions are a special case: If a new one is found in code, but does not exist in docs, the whole element needs to be added
            foreach (TripleSlashException tsException in tsMember.Exceptions)
            {
                DocsException dException = dMember.Exceptions.FirstOrDefault(x => x.Cref.EndsWith(tsException.Cref));
                bool          created    = false;

                if (dException == null)
                {
                    dException = dMember.SaveException(tsException.XEException);
                    AddedExceptions.AddIfNotExists($"{dException.Cref} in {dMember.DocId}");
                    created = true;
                }

                if (created || (!IsEmpty(tsException.Value) && IsEmpty(dException.Value)))
                {
                    PrintModifiedMember(string.Format("EXCEPTION ({0})", created ? "CREATED" : "MODIFIED"), dException.FilePath, tsException.Cref, dException.Cref, tsException.Value, dException.Value);

                    if (!created)
                    {
                        dException.Value = tsException.Value;
                    }
                    TotalModifiedIndividualElements++;
                    modified = true;
                }
            }

            foreach (TripleSlashTypeParam tsTypeParam in tsMember.TypeParams)
            {
                DocsTypeParam dTypeParam = dMember.TypeParams.FirstOrDefault(x => x.Name == tsTypeParam.Name);
                bool          created    = false;

                if (dTypeParam == null)
                {
                    ProblematicAPIs.AddIfNotExists($"TypeParam=[{tsTypeParam.Name}] in Member=[{dMember.DocId}]");
                    dTypeParam = dMember.SaveTypeParam(tsTypeParam.XETypeParam);
                    created    = true;
                }

                if (created || (!IsEmpty(tsTypeParam.Value) && IsEmpty(dTypeParam.Value)))
                {
                    PrintModifiedMember(string.Format("TYPE PARAM ({0})", created ? "CREATED" : "MODIFIED"), dTypeParam.FilePath, tsTypeParam.Name, dTypeParam.Name, tsTypeParam.Value, dTypeParam.Value);

                    if (!created)
                    {
                        dTypeParam.Value = tsTypeParam.Value;
                    }
                    TotalModifiedIndividualElements++;
                    modified = true;
                }
            }

            if (modified)
            {
                ModifiedAPIs.AddIfNotExists(dMember.DocId);
            }

            return(modified);
        }