1
2
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.RuleContext;
10 import net.sourceforge.pmd.lang.ast.Node;
11 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
13 import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
14 import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement;
15 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
16 import net.sourceforge.pmd.lang.java.ast.ASTName;
17 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
18 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
19 import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement;
20 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
21 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
22 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
23 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
24 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
25
26 import org.jaxen.JaxenException;
27
28
29
30
31
32
33
34 public class PreserveStackTraceRule extends AbstractJavaRule {
35
36
37
38
39 private static final String FIND_THROWABLE_INSTANCE = "./VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression"
40 + "[ClassOrInterfaceType[contains(@Image,'Exception')] and Arguments[count(*)=0]]";
41
42 private static final String FILL_IN_STACKTRACE = ".fillInStackTrace";
43
44 @Override
45 public Object visit(ASTCatchStatement catchStmt, Object data) {
46 String target = catchStmt.jjtGetChild(0).findChildrenOfType(ASTVariableDeclaratorId.class).get(0).getImage();
47
48 List<ASTThrowStatement> lstThrowStatements = catchStmt.findDescendantsOfType(ASTThrowStatement.class);
49 for (ASTThrowStatement throwStatement : lstThrowStatements) {
50 Node n = throwStatement.jjtGetChild(0).jjtGetChild(0);
51 if (n instanceof ASTCastExpression) {
52 ASTPrimaryExpression expr = (ASTPrimaryExpression) n.jjtGetChild(1);
53 if (expr.jjtGetNumChildren() > 1 && expr.jjtGetChild(1) instanceof ASTPrimaryPrefix) {
54 RuleContext ctx = (RuleContext) data;
55 addViolation(ctx, throwStatement);
56 }
57 continue;
58 }
59
60
61 ASTArgumentList args = throwStatement.getFirstDescendantOfType(ASTArgumentList.class);
62 if (args != null) {
63 Node parent = args.jjtGetParent().jjtGetParent();
64 if (parent instanceof ASTAllocationExpression) {
65
66 ck(data, target, throwStatement, parent);
67 } else {
68 ck(data, target, throwStatement, args);
69 }
70 } else {
71 Node child = throwStatement.jjtGetChild(0);
72 while (child != null && child.jjtGetNumChildren() > 0 && !(child instanceof ASTName)) {
73 child = child.jjtGetChild(0);
74 }
75 if (child != null) {
76 if (child instanceof ASTName && !target.equals(child.getImage())
77 && !child.hasImageEqualTo(target + FILL_IN_STACKTRACE)) {
78 Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((ASTName) child).getScope()
79 .getDeclarations(VariableNameDeclaration.class);
80 for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : vars.entrySet()) {
81 VariableNameDeclaration decl = entry.getKey();
82 List<NameOccurrence> occurrences = entry.getValue();
83 if (decl.getImage().equals(child.getImage())) {
84 if (!isInitCauseCalled(target, occurrences)) {
85 args = decl.getNode().jjtGetParent()
86 .getFirstDescendantOfType(ASTArgumentList.class);
87 if (args != null) {
88 ck(data, target, throwStatement, args);
89 }
90 }
91 }
92 }
93 } else if (child instanceof ASTClassOrInterfaceType) {
94 addViolation(data, throwStatement);
95 }
96 }
97 }
98 }
99 return super.visit(catchStmt, data);
100 }
101
102 private boolean isInitCauseCalled(String target, List<NameOccurrence> occurrences) {
103 boolean initCauseCalled = false;
104 for (NameOccurrence occurrence : occurrences) {
105 String image = null;
106 if (occurrence.getLocation() != null) {
107 image = occurrence.getLocation().getImage();
108 }
109 if (image != null && image.endsWith("initCause")) {
110 ASTPrimaryExpression primaryExpression = occurrence.getLocation().getFirstParentOfType(
111 ASTPrimaryExpression.class);
112 if (primaryExpression != null) {
113 ASTArgumentList args2 = primaryExpression.getFirstDescendantOfType(ASTArgumentList.class);
114 if (checkForTargetUsage(target, args2)) {
115 initCauseCalled = true;
116 break;
117 }
118 }
119 }
120 }
121 return initCauseCalled;
122 }
123
124 @Override
125 public Object visit(ASTVariableDeclarator node, Object data) {
126
127
128 try {
129 if (node.hasDescendantMatchingXPath(FIND_THROWABLE_INSTANCE)) {
130 String variableName = node.jjtGetChild(0).getImage();
131 ASTCatchStatement catchStmt = node.getFirstParentOfType(ASTCatchStatement.class);
132
133 while (catchStmt != null) {
134 List<Node> violations = catchStmt
135 .findChildNodesWithXPath("//Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = '"
136 + variableName + "']");
137 if (!violations.isEmpty()) {
138
139
140
141 if (!useInitCause(violations.get(0), catchStmt)) {
142 addViolation(data, node);
143 }
144 }
145
146
147 catchStmt = catchStmt.getFirstParentOfType(ASTCatchStatement.class);
148 }
149 }
150 return super.visit(node, data);
151 } catch (JaxenException e) {
152
153 throw new IllegalStateException(e);
154 }
155 }
156
157 private boolean useInitCause(Node node, ASTCatchStatement catchStmt) {
158
159 if (node != null && node.getImage() != null) {
160 return catchStmt
161 .hasDescendantMatchingXPath("./Block/BlockStatement/Statement/StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image = '"
162 + node.getImage() + ".initCause']");
163 }
164 return false;
165 }
166
167
168
169
170
171
172
173
174 private boolean checkForTargetUsage(String target, Node baseNode) {
175 boolean match = false;
176 if (target != null && baseNode != null) {
177 List<ASTName> nameNodes = baseNode.findDescendantsOfType(ASTName.class);
178 for (ASTName nameNode : nameNodes) {
179 if (target.equals(nameNode.getImage())) {
180 match = true;
181 break;
182 }
183 }
184 }
185 return match;
186 }
187
188 private void ck(Object data, String target, ASTThrowStatement throwStatement, Node baseNode) {
189 if (!checkForTargetUsage(target, baseNode)) {
190 RuleContext ctx = (RuleContext) data;
191 addViolation(ctx, throwStatement);
192 }
193 }
194 }