Основываясь на нескольких хороших статьях, я смог успешно создать несколько пользовательских правил 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
}
}
}
Обновление:
На основании дальнейшего тестирования выше указано НЕ безопасно.Нельзя использовать поля экземпляра для поддержания состояния.
При запуске StyleCop в проекте с несколькими файлами исходного кода несколько потоков будут совместно использовать один и тот же экземпляр анализатора.
Кроме того, с учетом приведенного ниже кода, несколько потоков и параллелизма также вступают в игру для каждого анализируемого документа исходного кода при выполнении вызова метода 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;
}
}