有没有被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。
代码作用域还没有考虑进去-_-!。