How to combine several StyleCop custom rules under one "Custom rules" node in StyleCop and concurrency settings

Based on a few good articles, I was able to successfully create some custom StyleCop rules. For reference, several articles that I found very useful in this thread are listed here:

I am using Visual Studio 2010 Ultimate version with StyleCop version 4.4.0.14.

Creating a Custom StyleCop Rule Creates a class file along with the corresponding XML file, which is used to add rules to StyleCop settings. When I do this, all of my user rules are executed correctly. However, I do not like that in the StyleCop settings tree you get several "Custom Rules" nodes, one for each XML file.

Skipping the details of the implementation of the various rules, this is what I did. Take the following two simple classes of custom rules for the corresponding XML files:

File: CustomRule1.cs

namespace StyleCop.CustomRules
{
    [SourceAnalyzer(typeof(CsParser))]
    public class CustomRule1 : SourceAnalyzer
    {
        public override void AnalyzeDocument(CodeDocument document)
        {
            Param.RequireNotNull(document, "document");
            CsDocument csDocument = document as CsDocument;
            if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
            {
                // Do something...
            }
        }
    }
}

File: CustomRule2.cs

namespace StyleCop.CustomRules
{
    [SourceAnalyzer(typeof(CsParser))]
    public class CustomRule2 : SourceAnalyzer
    {
        public override void AnalyzeDocument(CodeDocument document)
        {
            Param.RequireNotNull(document, "document");
            CsDocument csDocument = document as CsDocument;
            if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
            {
                // Do something...
            }
        }
    }
}

File: CustomRule1.xml

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
  <Description>
    These custom rules provide extensions to the ones provided with StyleCop.
  </Description>
  <Rules>
    <Rule Name="CustomRule1" CheckId="CR1001">
      <Context>Test rule 1.</Context>
      <Description>Test rule 1.</Description>
    </Rule>
  </Rules>
</SourceAnalyzer>

File: CustomRule2.xml

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
  <Description>
    These custom rules provide extensions to the ones provided with StyleCop.
  </Description>
  <Rules>
    <Rule Name="CustomRule2" CheckId="CR1002">
      <Context>Test rule 2.</Context>
      <Description>Test rule 2.</Description>
    </Rule>
  </Rules>
</SourceAnalyzer>

With the above, all (both) of my rules were correctly followed. The following appeared in the StyleCop settings tree (square brackets are a check box):

[] C#
    [] {} Custom Rules
        [] {} CR1001: CustomRule1
    [] {} Custom Rules
        [] {} CR1002: CustomRule2
    [] {} Documentation Rules
    [] {} Layout Rules
    etc.

, node " " StyleCop :

[] C#
    [] {} Custom Rules
        [] {} CR1001: CustomRule1
        [] {} CR1002: CustomRule2
    [] {} Documentation Rules
    [] {} Layout Rules
    etc.

" " node StyleCop, XML :

: CustomRule1.xml

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
  <Description>
    These custom rules provide extensions to the ones provided with StyleCop.
  </Description>
  <Rules>
    <Rule Name="CustomRule1" CheckId="CR1001">
      <Context>Test rule 1.</Context>
      <Description>Test rule 1.</Description>
    </Rule>
    <Rule Name="CustomRule2" CheckId="CR1002">
      <Context>Test rule 2.</Context>
      <Description>Test rule 2.</Description>
    </Rule>
  </Rules>
</SourceAnalyzer>

, , , CustomRule1, , () XML.

CustomRule2, XML :

namespace StyleCop.CustomRules
{
    [SourceAnalyzer(typeof(CsParser), "CustomRule1.xml")]
    public class CustomRule2 : SourceAnalyzer
    {
        public override void AnalyzeDocument(CodeDocument document)
        {
            Param.RequireNotNull(document, "document");
            CsDocument csDocument = document as CsDocument;
            if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
            {
                // Do nothing.
            }
        }
    }
}

, , XML, . StyleCop, CustomRule1.

?

Update:

, .

, , , , . , :

[SourceAnalyzer(typeof(CsParser))]
public class CustomRules : SourceAnalyzer
{
    private enum CustomRuleName
    {
        CustomRule1,
        CustomRule2
    }

    private CustomRuleName currentRule;

    public override void AnalyzeDocument(CodeDocument document)
    {
        Param.RequireNotNull(document, "document");
        CsDocument doc = document as CsDocument;

        // Do not analyze empty documents, code generated files and files that
        // are to be skipped.
        if (doc.RootElement == null || doc.RootElement.Generated)
        {
            return;
        }

        // Check Rule: CustomRule1
        this.currentRule = CustomRuleName.CustomRule1;
        doc.WalkDocument(VisitElement);

        // Check Rule: CustomRule2
        this.currentRule = CustomRuleName.CustomRule2;
        doc.WalkDocument(VisitElement);
    }

    private bool VisitElement(CsElement element, CsElement parentElement, object context)
    {
        if (this.currentRule == CustomRuleName.CustomRule1)
        {
            // Do checks only applicable to custom rule #1
        }
        else if (this.currentRule == CustomRuleName.CustomRule2)
        {
            // Do checks only applicable to custom rule #2
        }
    }
}

Update:

, . .

  • StyleCop .

  • , , concurrency , doc.WalkDocument(...). .

, , , VisitElement, StatementWalker ExpressionWalker .

[SourceAnalyzer(typeof(CsParser))]
public class CustomRules : SourceAnalyzer
{
    public override void AnalyzeDocument(CodeDocument document)
    {
        Param.RequireNotNull(document, "document");
        CsDocument doc = document as CsDocument;

        // Do not analyze empty documents, code generated files and files that
        // are to be skipped.
        if (doc.RootElement == null || doc.RootElement.Generated)
        {
            return;
        }

        IDictionary<string, Field> fields = new Dictionary<string, Field>();
        doc.WalkDocument(VisitElement, StatementWalker, ExpressionWalker, fields);
    }

    private bool VisitElement(CsElement element, CsElement parentElement, object context)
    {
        // Do something...
        return true;
    }

    private bool StatementWalker(Statement statement, Expression parentExpression, Statement parentStatement, CsElement parentElement, object context)
    {
        // Do something...
        return true;
    }

    private bool ExpressionWalker(Expression expression, Expression parentExpression, Statement parentStatement, CsElement parentElement, object context)
    {
        // Do something...
        return true;
    }
}
+3
2

( ). node .

node UI, , .

namespace StyleCop.CustomRules
{
    [SourceAnalyzer(typeof(CsParser))]
    public class MyCustomRules : SourceAnalyzer
    {
        public override void AnalyzeDocument(CodeDocument document)
        {
            // ...
            // code here can raise CR1001 as well as CR1002
        }
    }
}

: MyCustomRules.xml

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="My Custom Rules">
  <Description>
    These custom rules provide extensions to the ones provided with StyleCop.
  </Description>
  <Rules>
    <Rule Name="CustomRule1" CheckId="CR1001">
      <Context>Test rule 1.</Context>
      <Description>Test rule 1.</Description>
    </Rule>
    <Rule Name="CustomRule2" CheckId="CR1002">
      <Context>Test rule 2.</Context>
      <Description>Test rule 2.</Description>
    </Rule>
  </Rules>
</SourceAnalyzer>
+3

- , , . , - , :).

: 1. NamespaceMustBeginWithAllowedCompanyName      2. FieldNamesMustBeginWithUnderscore

: StyleCopExtensions.cs

private bool VisitElement(CsElement element, CsElement parentElement, object context)
    {
        #region Namespace rules
        if (!element.Generated && element.ElementType == ElementType.Namespace)
        {
            var @namespace = element.Declaration.Name;
            var companyName = NamespaceUtils.GetNamespaceToken(@namespace, NamespaceTokenType.Company);
            //Flag a "Violation" is the element doesn't start with an allowed company name
            if (!NamespaceUtils.ValidateNamespaceCompany(companyName))
            {
                AddViolation(parentElement, element.LineNumber, "NamespaceMustBeginWithAllowedCompanyName", companyName);
            }
        #endregion

        #region Fields rules
        if (!element.Generated && element.ElementType == ElementType.Field)
        {
            var fieldName = element.Declaration.Name;
            // Flag a violation if the instance variables are not prefixed with an underscore.
            if (element.ActualAccess != AccessModifierType.Public &&
                element.ActualAccess != AccessModifierType.Internal &&
                fieldName.ToCharArray()[0] != '_')
            {
                AddViolation(parentElement, element.LineNumber, "FieldNamesMustBeginWithUnderscore", fieldName);
            }
        }


        #endregion

        return true;
    }

Xml filename: StyleCopExtensions.xml - xml , . - , , string.Format(): : {0}, {1}, {N} xml.

+2

All Articles