/// <summary> /// Checks declaration comment blocks. /// </summary> /// <param name="file"> /// The <see cref="ICSharpFile"/> to use. /// </param> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to check. /// </param> /// <param name="options"> /// <see cref="OrderingOptions"/>Current options that we can reference. /// </param> private void CheckDeclarationDocumentation(ICSharpFile file, IDeclaration declaration, DocumentationOptions options) { Param.RequireNotNull(file, "file"); Param.RequireNotNull(declaration, "declaration"); Param.RequireNotNull(options, "options"); bool insertMissingElementDocOption = options.SA1600ElementsMustBeDocumented; bool documentationTextMustBeginWithACapitalLetter = options.SA1628DocumentationTextMustBeginWithACapitalLetter; bool documentationTextMustEndWithAPeriod = options.SA1629DocumentationTextMustEndWithAPeriod; bool elementDocumentationMustHaveSummary = options.SA1604ElementDocumentationMustHaveSummary; bool constructorSummaryDocBeginsWithStandardText = options.SA1642ConstructorSummaryDocumentationMustBeginWithStandardText; bool destructorSummaryDocBeginsWithStandardText = options.SA1643DestructorSummaryDocumentationMustBeginWithStandardText; bool propertyDocumentationMustHaveValueDocumented = options.SA1609PropertyDocumentationMustHaveValue; bool insertMissingParamTagOption = options.SA1611ElementParametersMustBeDocumented; bool genericTypeParametersMustBeDocumented = options.SA1618GenericTypeParametersMustBeDocumented; if (insertMissingElementDocOption && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1600)) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || (!declarationHeader.IsInherited && declarationHeader.HasEmptySummary && string.IsNullOrEmpty(declarationHeader.XmlNode.InnerText))) { this.InsertMissingDeclarationHeader(file, declaration); } } if (insertMissingParamTagOption && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1611)) { if (declaration is IConstructorDeclaration) { IConstructorDeclaration constructorDeclaration = declaration as IConstructorDeclaration; if (constructorDeclaration.ParameterDeclarations.Count > 0) { this.InsertMissingParamElement(constructorDeclaration); } } } if (elementDocumentationMustHaveSummary && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1604)) { this.InsertMissingSummaryElement(declaration); } DocumentationRulesConfiguration docConfig = this.GetDocumentationRulesConfig(file); // However it can be on/off depending on the file so we'd have to cache it per file bool ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("DocumentationTextMustBeginWithACapitalLetter"); if (documentationTextMustBeginWithACapitalLetter && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1628)) { this.EnsureDocumentationTextIsUppercase(declaration); } ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("DocumentationTextMustEndWithAPeriod"); if (documentationTextMustEndWithAPeriod && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1629)) { this.EnsureDocumentationTextEndsWithAPeriod(declaration); } if (constructorSummaryDocBeginsWithStandardText && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1642)) { this.EnsureConstructorSummaryDocBeginsWithStandardText(declaration as IConstructorDeclaration); } if (destructorSummaryDocBeginsWithStandardText && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1643)) { this.EnsureDestructorSummaryDocBeginsWithStandardText(declaration as IDestructorDeclaration); } if (declaration is IMethodDeclaration) { this.CheckMethodDeclarationDocumentation(declaration as IMethodDeclaration, options); } ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("PropertyDocumentationMustHaveValue"); if (propertyDocumentationMustHaveValueDocumented && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1609)) { if (declaration is IPropertyDeclaration) { this.InsertValueElement(declaration as IPropertyDeclaration); } } if (genericTypeParametersMustBeDocumented && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1618)) { this.InsertMissingTypeParamElement(declaration); } }
/// <summary> /// Removes a return element if it currently has one. /// </summary> /// <param name="memberDeclaration"> /// The <see cref="ITypeDeclaration"/> to check and fix. /// </param> public void RemoveReturnsElement(ITypeMemberDeclaration memberDeclaration) { DeclarationHeader declarationHeader = new DeclarationHeader(memberDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasReturns) { return; } declarationHeader.XmlNode.RemoveChild(declarationHeader.ReturnsXmlNode); declarationHeader.Update(); }
/// <summary> /// Ensures that the constructor documentation starts with the standard text summary. /// </summary> /// <remarks> /// Keeps the existing comment, but prepends the standard text. /// </remarks> /// <param name="constructorDeclaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureConstructorSummaryDocBeginsWithStandardText(IConstructorDeclaration constructorDeclaration) { if (constructorDeclaration == null) { return; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, constructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(constructorDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasSummary) { return; } string existingSummaryText = declarationHeader.SummaryXmlNode.InnerXml; bool parentIsStruct = Utils.IsContainingTypeAStruct(constructorDeclaration); int constructorParameterCount = constructorDeclaration.ParameterDeclarations.Count; string xmlComment = Utils.GetTextFromDeclarationHeader(declarationHeader.XmlNode); string structOrClass = parentIsStruct ? "struct" : "class"; string textWeShouldStartWith; if (constructorDeclaration.IsStatic) { textWeShouldStartWith = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForStaticConstructor, constructorDeclaration.DeclaredName, structOrClass); } else if (constructorDeclaration.GetAccessRights() == AccessRights.PRIVATE && constructorParameterCount == 0) { textWeShouldStartWith = string.Format( CultureInfo.InvariantCulture, HeaderSummaryForPrivateInstanceConstructor, constructorDeclaration.DeclaredName, structOrClass); } else { string constructorDescriptionText = Utils.CreateConstructorDescriptionText(constructorDeclaration, true); textWeShouldStartWith = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForInstanceConstructor, constructorDescriptionText, structOrClass); } if (constructorDeclaration.IsStatic) { string docStd = string.Format("Initializes the {0} class.", constructorDeclaration.DeclaredName); if (xmlComment == docStd) { existingSummaryText = string.Empty; } } if (!xmlComment.StartsWith(textWeShouldStartWith, StringComparison.Ordinal)) { string newSummaryText = Utils.CreateSummaryForConstructorDeclaration(constructorDeclaration); declarationHeader.SummaryXmlNode.InnerXml = newSummaryText + " " + existingSummaryText; declarationHeader.Update(); } }
/// <summary> /// Inserts a returns element to the element if its missing. /// </summary> /// <param name="memberDeclaration"> /// The <see cref="ITypeMemberDeclaration"/> to check and fix. /// </param> public void InsertReturnsElement(ITypeMemberDeclaration memberDeclaration) { Param.RequireNotNull(memberDeclaration, "memberDeclaration"); DeclarationHeader declarationHeader = new DeclarationHeader(memberDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; XmlNode returnsXmlNode = declarationHeader.ReturnsXmlNode; string valueText = string.Empty; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, memberDeclaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { valueText = string.Format("The {0}.", Utils.ConvertTextToSentence(memberDeclaration.DeclaredName).ToLower()); } if (declarationHeader.HasReturns) { if (string.IsNullOrEmpty(returnsXmlNode.InnerText.Trim())) { returnsXmlNode.InnerText = valueText; declarationHeader.Update(); } else { return; } } else { XmlNode valueNode = CreateNode(xmlNode, "returns"); valueNode.InnerText = valueText; xmlNode.AppendChild(valueNode); declarationHeader.Update(); } }
/// <summary> /// Inserts a value element to the element if its missing. /// </summary> /// <param name="propertyDeclaration"> /// The <see cref="IPropertyDeclaration"/> to check and fix. /// </param> public void InsertValueElement(IPropertyDeclaration propertyDeclaration) { DeclarationHeader declarationHeader = new DeclarationHeader(propertyDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; string valueText = string.Empty; XmlNode valueXmlNode = declarationHeader.ValueXmlNode; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, propertyDeclaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { valueText = string.Format("The {0}.", Utils.ConvertTextToSentence(propertyDeclaration.DeclaredName).ToLower()); } if (declarationHeader.HasValue) { if (string.IsNullOrEmpty(valueXmlNode.InnerText.Trim())) { valueXmlNode.InnerText = valueText; declarationHeader.Update(); } else { return; } } else { XmlNode valueNode = CreateNode(xmlNode, "value"); valueNode.InnerText = valueText; xmlNode.AppendChild(valueNode); declarationHeader.Update(); } }
/// <summary> /// Inserts a missing summary element. /// </summary> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to get comment from. /// </param> public void InsertMissingSummaryElement(IDeclaration declaration) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } string summaryText = string.Empty; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, declaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { summaryText = string.Format("The {0}.", Utils.ConvertTextToSentence(declaration.DeclaredName).ToLower()); } XmlNode summaryXmlNode = declarationHeader.SummaryXmlNode; if (declarationHeader.HasSummary) { if (string.IsNullOrEmpty(summaryXmlNode.InnerText.Trim())) { summaryXmlNode.InnerText = summaryText; declarationHeader.Update(); } else { return; } } else { XmlNode newChild = CreateNode(declarationHeader.XmlNode, "summary"); newChild.InnerText = summaryText; declarationHeader.XmlNode.InsertBefore(newChild, declarationHeader.XmlNode.FirstChild); declarationHeader.Update(); } }
/// <summary> /// Updates the summary to include all <c>typeparam</c> and remove any extra ones and in the correct order. /// </summary> /// <param name="declaration"> /// The <see cref="ITypeDeclaration"/> to check and fix. /// </param> public void InsertMissingTypeParamElement(IDeclaration declaration) { ITypeParametersOwner declaredElement = declaration.DeclaredElement as ITypeParametersOwner; if (declaredElement == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; Hashtable ht = new Hashtable(); foreach (ITypeParameter parameter in declaredElement.TypeParameters) { ht.Add(parameter.ShortName, null); if (declarationHeader.ContainsTypeParameter(parameter.ShortName)) { continue; } XmlNode parameterNode = CreateTypeParamNode(xmlNode, parameter.ShortName); XmlNodeList paramNodeList = xmlNode.SelectNodes("//typeparam"); if (paramNodeList != null) { XmlNode c = paramNodeList.Count == 0 ? declarationHeader.SummaryXmlNode : paramNodeList.Item(paramNodeList.Count - 1); xmlNode.InsertAfter(parameterNode, c); } } RemoveTypeParamsNotRequired(xmlNode, ht); ReorderTypeParams(xmlNode, declaredElement.TypeParameters); declarationHeader.Update(); }
/// <summary> /// Ensures the declaration passed has its comments beginning with a capital letter. /// </summary> /// <param name="declaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDocumentationTextIsUppercase(IDeclaration declaration) { IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, declaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } this.SwapToUpper(declarationHeader.XmlNode); declarationHeader.Update(); }
/// <summary> /// Insert a missing parameter element to the comment. /// </summary> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to check and fix. /// </param> public void InsertMissingParamElement(IDeclaration declaration) { Param.RequireNotNull(declaration, "declaration"); IParametersOwnerDeclaration parametersOwnerDeclaration = declaration as IParametersOwnerDeclaration; if (parametersOwnerDeclaration == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; Hashtable ht = new Hashtable(); IList<IParameterDeclaration> parameters = parametersOwnerDeclaration.ParameterDeclarations; if (parameters != null) { foreach (IParameterDeclaration parameter in parameters) { ht.Add(parameter.DeclaredName, null); if (declarationHeader.ContainsParameter(parameter.DeclaredName)) { continue; } XmlNodeList paramNodeList = xmlNode.SelectNodes("//param"); if (paramNodeList != null) { XmlNode c = paramNodeList.Count == 0 ? declarationHeader.SummaryXmlNode : paramNodeList.Item(paramNodeList.Count - 1); XmlNode parameterNode = CreateParamNode(xmlNode, parameter); xmlNode.InsertAfter(parameterNode, c); } } } RemoveParamsNotRequired(xmlNode, ht); ReorderParams(xmlNode, parameters); declarationHeader.Update(); }
/// <summary> /// Ensures the declaration passed has no blank lines unless inside code elements. /// </summary> /// <param name="declaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDocumentationHasNoBlankLines(IDeclaration declaration) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } this.RemoveBlankLines(declarationHeader.XmlNode); declarationHeader.Update(); }
/// <summary> /// Ensures that the destructor documentation starts with the standard text summary. /// </summary> /// <remarks> /// Keeps the existing comment, but prepends the standard text. /// </remarks> /// <param name="destructorDeclaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDestructorSummaryDocBeginsWithStandardText(IDestructorDeclaration destructorDeclaration) { if (destructorDeclaration == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(destructorDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasSummary) { return; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, destructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } string destructorDescriptionText = Utils.CreateDestructorDescriptionText(destructorDeclaration, true); string xmlComment = Utils.GetTextFromDeclarationHeader(declarationHeader.XmlNode); string textWeShouldStartWith = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForDestructor, destructorDescriptionText); if (!xmlComment.StartsWith(textWeShouldStartWith, StringComparison.Ordinal)) { string summaryText = Utils.CreateSummaryForDestructorDeclaration(destructorDeclaration); declarationHeader.SummaryXmlNode.InnerXml = Environment.NewLine + summaryText; declarationHeader.Update(); } }
/// <summary> /// Gets a string of the summary for this destructor. /// </summary> /// <param name="destructorDeclaration"> /// The destructor to produce the summary for. /// </param> /// <returns> /// A string of the destructor summary text. /// </returns> public static string CreateSummaryForDestructorDeclaration(IDestructorDeclaration destructorDeclaration) { string summaryText = string.Empty; DeclarationHeader declarationHeader = new DeclarationHeader(destructorDeclaration); if (declarationHeader.IsInherited) { return declarationHeader.XmlNode.InnerXml; } if (declarationHeader.HasSummary) { summaryText = declarationHeader.SummaryXmlNode.InnerXml; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, destructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return summaryText; } string destructorDescriptionText = CreateDestructorDescriptionText(destructorDeclaration, true); string newXmlText = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForDestructorXml, destructorDescriptionText); return newXmlText + " " + summaryText; }
/// <summary> /// Creates a new summary string for this constructor. /// </summary> /// <param name="constructorDeclaration"> /// The constructor to produce the summary for. /// </param> /// <returns> /// A string of the constructor summary text. /// </returns> public static string CreateSummaryForConstructorDeclaration(IConstructorDeclaration constructorDeclaration) { DeclarationHeader declarationHeader = new DeclarationHeader(constructorDeclaration); if (declarationHeader.IsInherited) { return declarationHeader.XmlNode.InnerXml; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, constructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return string.Empty; } bool parentIsStruct = IsContainingTypeAStruct(constructorDeclaration); string structOrClass = parentIsStruct ? "struct" : "class"; string xmlWeShouldInsert; if (constructorDeclaration.IsStatic) { xmlWeShouldInsert = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForStaticConstructorXml, constructorDeclaration.DeclaredName, structOrClass); } else if (constructorDeclaration.GetAccessRights() == AccessRights.PRIVATE && constructorDeclaration.ParameterDeclarations.Count == 0) { xmlWeShouldInsert = string.Format( CultureInfo.InvariantCulture, HeaderSummaryForPrivateInstanceConstructorXml, constructorDeclaration.DeclaredName, structOrClass); } else { string constructorDescriptionText = CreateConstructorDescriptionText(constructorDeclaration, true); xmlWeShouldInsert = string.Format(CultureInfo.InvariantCulture, HeaderSummaryForInstanceConstructorXml, constructorDescriptionText, structOrClass); } return xmlWeShouldInsert; }
/// <summary> /// Gets the current summary element for the declaration provided or null if missing. /// </summary> /// <param name="declaration"> /// The declaration to get the summary for. /// </param> /// <returns> /// A string of the summary or null. /// </returns> public static string GetSummaryForDeclaration(IDeclaration declaration) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasSummary) { return null; } return declarationHeader.SummaryXmlNode.InnerXml.Trim(); }