有沒有被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。
代碼作用域還沒有考慮進去-_-!。