View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.design;
5   
6   import java.util.List;
7   import java.util.Map;
8   
9   import net.sourceforge.pmd.lang.java.ast.ASTExpression;
10  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
11  import net.sourceforge.pmd.lang.java.ast.ASTName;
12  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
13  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
14  import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
15  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
16  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
17  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
18  
19  public class UnnecessaryLocalBeforeReturnRule extends AbstractJavaRule {
20  
21      @Override
22      public Object visit(ASTMethodDeclaration meth, Object data) {
23          // skip void/abstract/native method
24          if (meth.isVoid() || meth.isAbstract() || meth.isNative()) {
25              return data;
26          }
27          return super.visit(meth, data);
28      }
29  
30      @Override
31      public Object visit(ASTReturnStatement rtn, Object data) {
32          // skip returns of literals
33          ASTName name = rtn.getFirstDescendantOfType(ASTName.class);
34          if (name == null) {
35              return data;
36          }
37  
38          // skip 'complicated' expressions
39          if (rtn.findDescendantsOfType(ASTExpression.class).size() > 1 || rtn.findDescendantsOfType(ASTPrimaryExpression.class).size() > 1 || isMethodCall(rtn)) {
40              return data;
41          }
42  
43          Map<VariableNameDeclaration, List<NameOccurrence>> vars = name.getScope().getDeclarations(VariableNameDeclaration.class);
44          for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry: vars.entrySet()) {
45              VariableNameDeclaration key = entry.getKey();
46              List<NameOccurrence> usages = entry.getValue();
47              for (NameOccurrence occ: usages) {
48                  if (occ.getLocation().equals(name)) {
49                      // only check declarations that occur one line earlier
50                      if (key.getNode().getBeginLine() == name.getBeginLine() - 1) {
51                          String var = name.getImage();
52                          if (var.indexOf('.') != -1) {
53                              var = var.substring(0, var.indexOf('.'));
54                          }
55                          addViolation(data, rtn, var);
56                      }
57                  }
58              }
59          }
60          return data;
61      }
62  
63      /**
64       * Determine if the given return statement has any embedded method calls.
65       *
66       * @param rtn
67       *          return statement to analyze
68       * @return true if any method calls are made within the given return
69       */
70      private boolean isMethodCall(ASTReturnStatement rtn) {
71       List<ASTPrimarySuffix> suffix = rtn.findDescendantsOfType( ASTPrimarySuffix.class );
72       for ( ASTPrimarySuffix element: suffix ) {
73          if ( element.isArguments() ) {
74            return true;
75          }
76        }
77        return false;
78      }
79  }