基于几篇好文章,我已经能够成功创建一些自定义 StyleCop 规则。作为参考,这里列出了我发现关于该主题非常有用的几篇文章:
- 如何实施自定义 StyleCop 规则 https://sites.google.com/a/rees.biz/main/Home/customstylecoprules
- 为 Microsoft Source Analyzer 创建自定义规则 - 第一部分 http://www.lovethedot.net/2008/05/creating-custom-rules-for-microsoft.html
- 为 Microsoft Source Analyzer 创建自定义规则 - 第 II 部分 http://www.lovethedot.net/2008/05/creating-custom-rules-for-microsoft_27.html
- 为 Microsoft Source Analyzer 创建自定义规则 - 第 III 部分 http://www.lovethedot.net/2008/05/creating-custom-rules-for-microsoft_6976.html
我正在使用 Visual Studio 2010 Ultimate 版本和 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.
通过将两个 XML 文件合并为一个,我能够将规则合并到 StyleCop 设置中的单个“自定义规则”节点中,如下所示:
文件: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>
然而,一旦我这样做了,只有一个自定义规则被执行,那就是自定义规则1,类(文件)名与 XML 文件名匹配的规则。
我尝试将属性设置为自定义规则2表示 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 设置中,但仅自定义规则1被处决。
我该如何解决这个问题?
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:
根据进一步测试,上述结果是NOT安全的。不能使用实例字段来维护状态。
当在具有多个源代码文件的项目上运行 StyleCop 时,多个线程将共享分析器的同一实例。
此外,考虑到下面的代码,当调用时,多线程和并发也会在正在分析的每个源代码文档上发挥作用doc.WalkDocument(...)
方法做出来。每个表达式树遍历器都在自己的线程上运行。
换句话说,除了可以在多个线程上同时分析多个源代码文件之外,回调VisitElement
, StatementWalker
and 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;
}
}