有沒有被Source Review折磨過?有沒有被千奇百怪的寫法折磨過?
發(fā)現(xiàn)對中國的coder來說,規(guī)范基本等于0。明明代碼規(guī)范里說不許寫但是照寫的大有人在。
用人力去Review這些代碼有時候容易遺漏,有時候沒有這么多力氣去看幾千行代碼。

所以工具必不可少。這里講一個檢查字符串相加的。

字符串相加,例如
String str1 = "abc";
String str 
= str1 + "123";
很多情況下是違反代碼規(guī)范的。知道這種+號會被javac翻譯成StringBuffer.append,所以我才不能用findbugs,8過代碼規(guī)范不等于語言。

普通的做法可以檢查出""這種的字符串相加,我這里寫了一個很垃圾的能檢查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是定義一個變量

當(dāng)遇到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);
        }
    }


整個邏輯其實就是,當(dāng)你AST看到+號的時候,看看前面,后面是不是文字類型。如果不是文字類型但是是一個標(biāo)示符的話,就看下
vartbl里是不是有這個標(biāo)示符,然后看看他的類型是不是String。

代碼作用域還沒有考慮進去-_-!。