2011-05-15 52 views
3

基於幾篇很好的文章,我已經能夠成功創建幾個自定義StyleCop規則。作爲參考,幾篇文章,我發現關於這個主題非常有用的是這裏列出:如何在StyleCop設置和併發中的單個「自定義規則」節點下合併多個自定義StyleCop規則

我使用Visual Studio 2010終極版以及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> 

但是,一旦我這樣做,只有一個自定義規則得到執行,這是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被執行。

我該如何解決這個問題?

更新:基於公認的答案

,我參加了一個單人分析儀內檢查我所有的自定義規則的做法。

從我的理解來看,每個表達樹walker運行在它自己的線程上,因此狀態在這個過程中不能被輕易地共享。如果我使用單個分析儀的方法,我可以安全地執行以下操作嗎?

[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(...)方法進行調用時,多個線程和併發也會在分析的每個源代碼文檔中發揮作用。每個表達樹walker運行在它自己的線程上。

換言之,除了以下事實:多個源代碼文件可以同時在多個線程進行分析,回調VisitElementStatementWalkerExpressionWalker也在單獨的線程來執行。

[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

通常分析儀包含多個規則(否則它會很奇怪)。 每個分析器都顯示爲設置UI中的單獨節點。

如果你想在設置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> 
+0

感謝您的意見。請參閱我的更新。在調用doc.WalkDocument(...)時,維護哪些規則正在檢查的狀態是否安全? – Elan 2011-05-15 22:23:20

+0

@Elan:StyleCop分析器提供了豐富多樣的方法,可以讓你用數百種方式編寫分析器代碼。在你的特定模式中,似乎你不需要currentRule字段。只需從VisitElement()方法開始走路並引發適當的錯誤。 – 2011-05-16 08:29:06

+0

謝謝,我很好地使用了一臺分析儀,這很合理。如果我共享相同的VistElement()方法,那麼我必須區分正在運行的規則,以便檢查適用於正在驗證的規則。如果我爲每個規則創建單獨的VisitElement()回調,那麼很自然我就不需要currentRule字段。 – Elan 2011-05-16 15:25:20

2

如果有人有興趣這個主題,我做了一些自定義的規則,我也能組合這些規則。事實上,如果有人有興趣將參數傳遞給警告消息,我的規則也會這樣做:)。

規則: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()'相同的方式使用消息中發送的參數:只要在xml文件中包含{0},{1},{N}即可。