Como combinar várias regras personalizadas do StyleCop em um único nó "Regras personalizadas" nas configurações e simultaneidade do StyleCop

Com base em vários bons artigos, consegui criar com sucesso algumas regras personalizadas do StyleCop. Para referência, alguns artigos que achei muito úteis sobre esse tópico estão listados aqui:

Como implementar uma regra StyleCop personalizadariando regras personalizadas para o Microsoft Source Analyzer - Parte Iriando regras personalizadas para o Microsoft Source Analyzer - Parte IIriando regras personalizadas para o Microsoft Source Analyzer - Parte III

Estou usando o Visual Studio 2010 Ultimate edition junto com a versão 4.4.0.14 do StyleCo

Criar uma regra personalizada do StyleCop exige a criação de um arquivo de classe junto com o arquivo XML correspondente, usado para adicionar as regras às configurações do StyleCop. Quando faço isso, todas as minhas regras personalizadas são executadas corretamente. No entanto, o que não gosto sobre isso é que, na árvore de configurações do StyleCop, você acaba recebendo vários nós "Regras personalizadas", um para cada arquivo XM

Ignorando os detalhes de implementação das diferentes regras, aqui está o que eu fiz. Vamos seguir as duas classes de regras personalizadas simples abaixo, ao longo de seus arquivos XML correspondentes:

Arquivo: 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...
            }
        }
    }
}

Arquivo: 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...
            }
        }
    }
}

Arquivo: 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>

Arquivo: 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>

Com o exposto, todas as minhas regras foram executadas corretamente. O seguinte apareceu na árvore de configurações do StyleCop (os colchetes representam uma caixa de seleção):

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

O que eu gostaria é ter minhas regras personalizadas em um nó chamado "Regras personalizadas" no arquivo de configurações do StyleCop da seguinte maneira:

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

Consegui combinar as regras em um único nó "Regras personalizadas" nas configurações do StyleCop combinando os dois arquivos XML em um, da seguinte maneira:

Arquivo: 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>

No entanto, uma vez que fiz isso, APENAS uma regra personalizada foi executada e foi CustomRule1, a regra em que o nome da classe (arquivo) corresponde ao nome do arquivo XM

Tentei definir o atributo em CustomRule2 para indicar o arquivo XML da seguinte maneira:

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.
            }
        }
    }
}

A configuração do atributo como mostrado acima no arquivo XML também não resolveu esse problema. Ambas as regras aparecem nas configurações do StyleCop, mas apenas CustomRule1 é executado.

Como posso resolver isto

Atualizar

om base na resposta aceita, tomei a abordagem de verificar todas as minhas regras personalizadas em um único analisado

Do meu entendimento, cada walker de árvore de expressão é executado em seu próprio thread e, portanto, o estado não pode ser facilmente compartilhado durante o processo. Se eu seguir a abordagem de usar um único analisador, posso fazer o seguinte com segurança?

[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
        }
    }
}

Atualizar

Com base em testes adicionais, o acima é seguro. Não se pode usar campos de instância para manter o estad

Ao executar o StyleCop em um projeto com vários arquivos de código-fonte, vários threads compartilharão a mesma instância do analisado

Além disso, dado o código abaixo, vários threads e simultaneidade também entram em jogo em cada documento de código-fonte sendo analisado quando a chamada paradoc.WalkDocument(...) método @ é criado. Cada andador de árvore de expressão é executado em seu próprio encadeament

Em outras palavras, além do fato de que vários arquivos de código-fonte podem ser analisados simultaneamente em vários threads, os retornos de chamadaVisitElement, StatementWalker eExpressionWalker também são executados em threads separado

[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;
    }
}

questionAnswers(2)

yourAnswerToTheQuestion