有没有被Source Review折磨过?有没有被千奇百怪的写法折磨过?
发现对中国的coder来说,规范基本等于0。明明代码规范里说不许写但是照写的大有人在。
用人力去Review这些代码有时候容易遗漏,有时候没有这么多力气去看几千行代码。

所以工具必不可少。这里讲一个检查字符串相加的。

字符串相加,例如
<!---->String str1  =   " abc " ;
String str 
=  str1  +   " 123 " ;
很多情况下是违反代码规范的。 知道这种+号会被javac翻译成StringBuffer.append,所以我才不能用findbugs,8过代码规范不等于语言。

普通的做法可以检查出""这种的字符串相加,我这里写了一个很垃圾的能检查 String变量相加的情况。变量范围仅限于local变量。目前来说是够了。

<!---->public   class  StringPlusCheck  extends  Check {


    
public  StringPlusCheck(){
        
    }
   
    
public   int [] getDefaultTokens() {

        
return   new   int [] {TokenTypes.PLUS, TokenTypes.PLUS_ASSIGN,TokenTypes.VARIABLE_DEF};
    }


    
public   void  visitToken(DetailAST aAST) {
        
switch (aAST.getType()) {
            
case  TokenTypes.VARIABLE_DEF:
                processVarDef(aAST);
                
break ;
            
default :
                AST firstChild 
=  aAST.getFirstChild();
                AST secondChild 
=  firstChild.getNextSibling();
                
if (checkType(firstChild)  ||  checkType(secondChild)) {
                    log(aAST.getLineNo(), aAST.getColumnNo(), 
" zhu tou you wrote a string plus " , aAST.getText());
                }
       }
    }

    
private   void  processVarDef(DetailAST aAST) {

        AST child 
=  aAST.getFirstChild();
        String varName 
=   null ;
        String varType 
=   null ;
        
while (child  !=   null ) {
            
switch (child.getType()) {
                
case  TokenTypes.TYPE:
                    varType 
=  ASTUtils.stringifyNode(child.getFirstChild());
                    
break ;
                
case  TokenTypes.IDENT:
                    varName 
=  child.getText();
                    
break ;
                
default :
                    
break ;
            }
            child 
=  child.getNextSibling();
        }
        vartbl.put(varName, varType);
    }


    
private   boolean  checkType(AST ast) {
        
switch (ast.getType()) {
            
case  TokenTypes.STRING_LITERAL:
            
case  TokenTypes.CHAR_LITERAL:
                
return   true ;
            
case  TokenTypes.IDENT:
                String varName 
=  ast.getText();
                String varType 
=  (String)vartbl.get(varName);
                
if (varType  !=   null   &&  varType.endsWith( " String " )) {
                    
return   false ;
                }
            
default :
                
return   false ;
        }
    }

}


需要一点点AST方面的知识。 TokenTypes.PLUS就是+号, PLUS_ASSIGN就是+=, VARIABLE_DEF是定义一个变量

当遇到VARIABLE_DEF的时候呢,就去把它的TYPE和NAME储存到vartbl里去。
stringifyNode的时候要递归。因为有人会写java.lang.String abc = "123";(-_-!一切皆有可能)


<!---->    public   static  String stringifyNode(AST ast) {

        StringBuffer result 
=   new  StringBuffer();
        _stringifyNode(ast, result);
        
return  result.toString();
    }


    
private   static   void  _stringifyNode(AST ast, StringBuffer sbf) {

        
if (ast  ==   null ) {
            
return ;
        }
        
int  childcnt  =  ast.getNumberOfChildren();
        
if (childcnt  >   0 ) {
            _stringifyNode(ast.getFirstChild(), sbf);
        }
        sbf.append(ast.getText());
        
if (childcnt  >   0 ) {
            _stringifyNode(ast.getFirstChild().getNextSibling(), sbf);
        }
    }


整个逻辑其实就是,当你AST看到+号的时候,看看前面,后面是不是文字类型。如果不是文字类型但是是一个标示符的话,就看下
vartbl里是不是有这个标示符,然后看看他的类型是不是String。

代码作用域还没有考虑进去-_-!。