Как объединить несколько пользовательских правил StyleCop в одном узле «Пользовательские правила» в настройках StyleCop и параллелизме - PullRequest
4 голосов
/ 15 мая 2011

Основываясь на нескольких хороших статьях, я смог успешно создать несколько пользовательских правил StyleCop.Для справки, несколько статей, которые я нашел очень полезными по этой теме, перечислены здесь:

Я использую Visual Studio 2010 Ultimate Edition вместе с StyleCop версии 4.4.0.14.

Создание настраиваемого правила StyleCop требует создания файла класса вместе ссоответствующий файл XML, который используется для добавления правил в настройки StyleCop.Когда я делаю это, все мои пользовательские правила выполняются правильно.Однако что мне не нравится в этом, так это то, что в дереве настроек StyleCop вы получаете несколько узлов «Пользовательские правила», по одному на каждый XML-файл.

Здесь пропускаются детали реализации различных правил.это то, что я сделалДавайте возьмем два следующих простых класса пользовательских правил вместе с соответствующими им файлами XML:

Файл: 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...
            }
        }
    }
}

Файл: 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...
            }
        }
    }
}

Файл: 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>

Файл: 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>

С учетом вышеизложенного все (оба) мои правила были выполнены правильно.В дереве настроек StyleCop появилось следующее (квадратные скобки обозначают флажок):

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

Мне бы хотелось, чтобы мои пользовательские правила находились под одним узлом под названием «Пользовательские правила» в файле настроек StyleCop.следующим образом:

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

Мне удалось объединить правила в один узел «Пользовательские правила» в настройках 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 , правило, в котором класс (файл)name соответствует имени файла 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 .

Как мне решить эту проблему?

Обновление:

Основываясь на принятом ответе, я применил подход проверки всех своих пользовательских правил в одном анализаторе.

Насколько я понимаю, каждый обходчик дерева выражений работает в своем собственном потоке, и, таким образом, состояние не может быть легко разделеново время процесса.Если я придерживаюсь подхода использования одного анализатора, могу ли я безопасно выполнить следующее?

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

Обновление:

На основании дальнейшего тестирования выше указано НЕ безопасно.Нельзя использовать поля экземпляра для поддержания состояния.

  1. При запуске StyleCop в проекте с несколькими файлами исходного кода несколько потоков будут совместно использовать один и тот же экземпляр анализатора.

  2. Кроме того, с учетом приведенного ниже кода, несколько потоков и параллелизма также вступают в игру для каждого анализируемого документа исходного кода при выполнении вызова метода 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;
    }
}

Ответы [ 2 ]

3 голосов
/ 16 мая 2011

Обычно анализатор содержит более одного правила (в противном случае это было бы довольно странно).Каждый анализатор отображается как отдельный узел в пользовательском интерфейсе настроек.

Если вы хотите, чтобы в пользовательском интерфейсе настроек был один узел, вам, безусловно, нужен только один анализатор, который будет выполнять обе ваши проверки.

Файл: 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>
2 голосов
/ 24 октября 2011

Если кто-то заинтересован в этой теме, я создал несколько пользовательских правил, а также смог сгруппировать эти правила.На самом деле, если кто-то заинтересован в передаче параметров в предупреждающие сообщения, мои правила также делают это:).

Правила: 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: StyleCopExtensions.xml - Аналогично другим файлам XML, опубликованным ниже.- Вы можете использовать параметры, отправленные в сообщениях, таким же образом, как это делает 'string.Format ()': просто включите {0}, {1}, {N} внутри элемента в файле xml.

...