有没有被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。

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