编写StyleCop自定义规则教程(一)---编写中文备注的简单校验规则

基本步骤:

1、 新建Dll项目,引用StyleCop.DllStyleCop.Csharp.Dll

2、 自定义的规则校验类从SourceAnalyzer继承,必须实现public virtual void AnalyzeDocument(CodeDocument document)方法。

3、 在需要触发违反规则的地方使用方法AddViolation

4、 在项目中增加一个xml嵌入资源,该资源的文件名必须与校验类同名。该xml可以定义校验类显示在StyleCop Setting的规则、规则描述和参数等。

5、 编译成功,将Dll复制到StyleCopy的目录下。

 

    StyleCopDocumnetaion Rules对代码的文档有一系列的要求,譬如SA1623 要求属性的备注文档开头必须注明whether the property exposes get or set accessors。其校验的实现方式是在备注的开头判断是否有属性访问权对应的Get/ Set字符。中文用户是不大适应用GetSet作备注的,因此以下一个简单例子实现一个判断中文备注的规则,代替原有的SA1623

     由于每个Rule都必须有唯一的RuleNameRuleIDRuleName只是用于内部,不被用户看见。RuleID2位大写字母开头和4位数字组成。SA1623就是RuleID。例子使用MY1623StyleCop原有的RuleID相对。

 

     覆盖AnalyzeDocument方法

1  public   override   void  AnalyzeDocument(CodeDocument document)
2          {
3              Param.RequireNotNull(document,  " document " );
4              CsDocument document2  =  (CsDocument)document;
5               if  ((document2.RootElement  !=   null &&   ! document2.RootElement.Generated)
6              {
7                  document2.WalkDocument( new  CodeWalkerElementVisitor < object > ( this .CheckDocumentationForElement),  null );
8              }
9          }

 

WalkDocumnet方法是遍历代码中的各个节点(方法、属性等)。CheckDocumentationForElementelement.ElementType == ElementType.Property是执行具体操作的方法。这里编写的规则只针对属性。


private   bool  CheckDocumentationForElement(CsElement element, CsElement parentElement, Object settings)
        {
            
if  ( base .Cancel)
            {
                
return   false ;
            }

            
if  ( ! element.Generated)
            {
                
if  (element.ElementType  ==  ElementType.Property)
                {
                    
this .ParseHeader(element, element.Header, element.LineNumber,  false );
                }
            }
            
return   true ;
        }

 

private   readonly   string  headerSummaryForGetAndSetAccessor  =   " 可读写 " ;
        
private   readonly   string  headerSummaryForGetAccessor  =   " 只读 " ;
        
private   readonly   string  headerSummaryForSetAccessor  =   " 只写 " ;
        
        
private   void  ParseHeader(CsElement element, XmlHeader header,  int  lineNumber,  bool  partialElement)
        {
            XmlDocument doc 
=   this .LoadHeaderIntoDocument(element, header, lineNumber);
            
if  ((doc  !=   null &&  (element.ElementType  ==  ElementType.Property))
            {
                
this .CheckPropertySummaryFormatting(element  as  Property, doc);
            }
        }

        
private  XmlDocument LoadHeaderIntoDocument(CsElement element, XmlHeader header,  int  lineNumber)
        {
            XmlDocument document 
=   new  XmlDocument();
            
try
            {
                
string  xml  =   " <root> "   +  header.Text  +   " </root> " ;
                document.LoadXml(xml);
            }
            
catch  (XmlException exception)
            {
                
base .AddViolation(element, lineNumber, Rules.DocumentationMustContainValidXml,  new   object [] { exception.Message });
                document 
=   null ;
            }
            
catch  (Exception exception)
            {
                
base .AddViolation(element, lineNumber, Rules.DocumentationMustContainValidXml,  new   object [] { exception.Message });
                document 
=   null ;
            }
            
return  document;
        }
        
        
private   void  CheckPropertySummaryFormatting(Property property, XmlDocument doc)
        {
            XmlNode node 
=  doc.SelectSingleNode( " root/summary " );
            
if  (node  !=   null )
            {
                
bool  flag  =  ((property.ReturnType.Text  ==   " bool " ||  (property.ReturnType.Text  ==   " Boolean " ))  ||  (property.ReturnType.Text  ==   " System.Boolean " );
                
if  (property.GetAccessor  !=   null )
                {
                    
if  ((property.SetAccessor  ==   null ||  ((property.SetAccessor.AccessModifier  !=  property.AccessModifier)  &&  (((property.AccessModifier  ==  AccessModifierType.Private)  ||  (property.SetAccessor.AccessModifier  !=  AccessModifierType.Private))  ||  property.SetAccessor.Declaration.ContainsModifier( new  CsTokenType[] { CsTokenType.Private }))))
                    {
                        
string  str2  =  headerSummaryForGetAccessor;
                        
string  str3  =  node.InnerText.TrimStart( new   char [ 0 ]);                        
                        
if  ( ! CompareTextWithColon(str3, str2))
                        {
                            
base .AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors,  new   object [] { str2 });
                        }
                    }
                    
else
                    {
                        
string  str  =  headerSummaryForGetAndSetAccessor;                        
                        
if  ( ! CompareTextWithColon(node.InnerText.TrimStart( new   char [ 0 ]), str))
                        {
                            
base .AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors,  new   object [] { str });
                        }
                    }
                }
                
else   if  (property.SetAccessor  !=   null )
                {
                    
string  str5  =  headerSummaryForSetAccessor;                   
                    
if  ( ! CompareTextWithColon(node.InnerText.TrimStart( new   char [ 0 ]), str5))
                    {
                        
base .AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors,  new   object [] { str5 });
                    }
                }
            }
        }

        
private   bool  CompareTextWithColon( string  input,  string  pattern)
        {
            
return  Regex.IsMatch(input, pattern  +   " [::] " , RegexOptions.Singleline);
        }

 

对应的资源xml

View Code
<? xml version="1.0" encoding="utf-8"  ?>
< SourceAnalyzer  Name ="My Documentation Rules" >
  
< Description >
    Rules which verify the content and formatting of code documentation.
  
</ Description >   
  
< Rules >       
      
< Rule  Name ="DocumentationMustContainValidXml"  CheckId ="HD1603" >
        
< Context > The documentation header is composed of invalid Xml: {0} </ Context >
        
< Description > Indicates that a documentation header is composed of invalid Xml and cannot be parsed. </ Description >
      
</ Rule >       
      
< Rule  Name ="PropertySummaryDocumentationMustMatchAccessors"  CheckId ="HD1623" >
        
< Context > 属性的摘要文档说明必须使用“{0}”开头 </ Context >
        
< Description > Validates that a property's summary description text begins with the correct syntax, depending upon whether the property exposes get or set accessors. </ Description >
      
</ Rule >
  
</ Rules >
</ SourceAnalyzer >

 其中Description节点的内容是显示在Stylecop Setting窗口下方的文字。Context是违反规则时,显示在VS 错误列表的文字。

 

附上代码:/Files/Byeah/MyFirstRule.zip

 

下一篇:《编写StyleCop自定义规则教程(二)---校验规则增加配置参数 》

 

你可能感兴趣的:(style)