1
2
3
4
5 package com.qulice.pmd.rules;
6
7 import java.util.Set;
8 import net.sourceforge.pmd.lang.ast.Node;
9 import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
10 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
11 import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
12 import net.sourceforge.pmd.lang.java.ast.ASTResource;
13 import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
14 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
15 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
16 import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
17
18
19
20
21
22
23
24 public final class CloseInlineResourceRule extends AbstractJavaRulechainRule {
25
26
27
28
29 private static final Set<String> ALLOWED = Set.of(
30 "java.io.ByteArrayOutputStream",
31 "java.io.ByteArrayInputStream",
32 "java.io.StringWriter",
33 "java.io.CharArrayWriter",
34 "java.util.stream.Stream",
35 "java.util.stream.IntStream",
36 "java.util.stream.LongStream",
37 "java.util.stream.DoubleStream"
38 );
39
40
41
42
43 public CloseInlineResourceRule() {
44 super(ASTConstructorCall.class, ASTMethodCall.class);
45 }
46
47 @Override
48 public Object visit(final ASTConstructorCall call, final Object data) {
49 if (CloseInlineResourceRule.leaksInline(call)) {
50 this.asCtx(data).addViolation(call);
51 }
52 return data;
53 }
54
55 @Override
56 public Object visit(final ASTMethodCall call, final Object data) {
57 if (CloseInlineResourceRule.leaksInline(call)
58 && CloseInlineResourceRule.hasCloseableArgument(call)) {
59 this.asCtx(data).addViolation(call);
60 }
61 return data;
62 }
63
64 private static boolean leaksInline(final ASTExpression expr) {
65 boolean result = false;
66 if (CloseInlineResourceRule.closeable(expr)
67 && !CloseInlineResourceRule.allowed(expr)) {
68 result = CloseInlineResourceRule.unmanagedInline(expr);
69 }
70 return result;
71 }
72
73 private static boolean closeable(final ASTExpression expr) {
74 return TypeTestUtil.isA(AutoCloseable.class, expr);
75 }
76
77 private static boolean allowed(final ASTExpression expr) {
78 return CloseInlineResourceRule.ALLOWED.stream()
79 .anyMatch(type -> TypeTestUtil.isExactlyA(type, expr));
80 }
81
82 private static boolean unmanagedInline(final ASTExpression expr) {
83 return CloseInlineResourceRule.inline(expr)
84 && !CloseInlineResourceRule.closedDirectly(expr)
85 && !CloseInlineResourceRule.returnedDirectly(expr);
86 }
87
88 private static boolean hasCloseableArgument(final ASTMethodCall call) {
89 return call.getArguments().descendants(ASTExpression.class)
90 .any(CloseInlineResourceRule::closeable);
91 }
92
93 private static boolean inline(final ASTExpression expr) {
94 return expr.ancestors(ASTResource.class).isEmpty()
95 && expr.ancestors(ASTVariableDeclarator.class).isEmpty();
96 }
97
98 private static boolean closedDirectly(final ASTExpression expr) {
99 boolean found = false;
100 final Node parent = expr.getParent();
101 if (parent instanceof ASTMethodCall call) {
102 found = "close".equals(call.getMethodName())
103 && expr.equals(call.getQualifier());
104 }
105 return found;
106 }
107
108 private static boolean returnedDirectly(final ASTExpression expr) {
109 return expr.getParent() instanceof ASTReturnStatement;
110 }
111 }