IDEADEV-31824 (Incorrect "manual array copy" warning)
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / resources / ResourceInspection.java
blob51869d3843836536feae9f9d18ff99804210f46c
1 /*
2 * Copyright 2008 Bas Leijdekkers
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.siyeh.ig.resources;
18 import com.intellij.psi.*;
19 import com.intellij.psi.util.PsiTreeUtil;
20 import com.intellij.psi.util.PsiUtil;
21 import com.siyeh.HardcodedMethodConstants;
22 import com.siyeh.ig.BaseInspection;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.annotations.Nullable;
26 public abstract class ResourceInspection extends BaseInspection {
28 @Nullable
29 protected static PsiVariable getVariable(
30 @NotNull PsiElement element) {
31 if (element instanceof PsiAssignmentExpression) {
32 final PsiAssignmentExpression assignment =
33 (PsiAssignmentExpression) element;
34 final PsiExpression lhs = assignment.getLExpression();
35 if (!(lhs instanceof PsiReferenceExpression)) {
36 return null;
38 final PsiReferenceExpression referenceExpression =
39 (PsiReferenceExpression) lhs;
40 final PsiElement referent = referenceExpression.resolve();
41 if (referent == null || !(referent instanceof PsiVariable)) {
42 return null;
44 return (PsiVariable) referent;
45 } else if (element instanceof PsiVariable) {
46 return (PsiVariable) element;
47 } else {
48 return null;
52 protected static PsiElement getExpressionParent(PsiExpression expression) {
53 PsiElement parent = expression.getParent();
54 while (parent instanceof PsiParenthesizedExpression ||
55 parent instanceof PsiTypeCastExpression) {
56 parent = parent.getParent();
58 return parent;
61 protected static boolean isSafelyClosed(@Nullable PsiVariable boundVariable,
62 PsiExpression creationContext
63 ) {
64 if (boundVariable == null) {
65 return false;
67 final PsiStatement statement =
68 PsiTreeUtil.getParentOfType(creationContext, PsiStatement.class);
69 if (statement == null) {
70 return false;
72 final PsiStatement nextStatement =
73 PsiTreeUtil.getNextSiblingOfType(statement,
74 PsiStatement.class);
75 if (!(nextStatement instanceof PsiTryStatement)) {
76 // exception in next statement can prevent closing of the resource
77 return false;
79 final PsiTryStatement tryStatement = (PsiTryStatement) nextStatement;
80 return resourceIsClosedInFinally(tryStatement, boundVariable);
83 protected static boolean resourceIsClosedInFinally(
84 @NotNull PsiTryStatement tryStatement,
85 @NotNull PsiVariable boundVariable) {
86 final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
87 if (finallyBlock == null) {
88 return false;
90 final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
91 if (tryBlock == null) {
92 return false;
94 final CloseVisitor visitor = new CloseVisitor(boundVariable);
95 finallyBlock.accept(visitor);
96 return visitor.containsClose();
99 protected static boolean isResourceEscapedFromMethod(
100 PsiVariable boundVariable, PsiElement context){
101 // poor man dataflow
102 final PsiMethod method =
103 PsiTreeUtil.getParentOfType(context, PsiMethod.class, true,
104 PsiMember.class);
105 if(method == null){
106 return false;
108 final PsiCodeBlock body = method.getBody();
109 if(body == null){
110 return false;
112 final EscapeVisitor visitor = new EscapeVisitor(boundVariable);
113 body.accept(visitor);
114 return visitor.isEscaped();
117 private static class CloseVisitor extends JavaRecursiveElementVisitor {
119 private boolean containsClose = false;
120 private final PsiVariable resource;
121 private final String resourceName;
123 private CloseVisitor(PsiVariable resource) {
124 this.resource = resource;
125 this.resourceName = resource.getName();
128 @Override
129 public void visitElement(@NotNull PsiElement element) {
130 if (!containsClose) {
131 super.visitElement(element);
135 @Override
136 public void visitMethodCallExpression(
137 @NotNull PsiMethodCallExpression call) {
138 if (containsClose) {
139 return;
141 super.visitMethodCallExpression(call);
142 if (!isResourceClose(call, resource)) {
143 return;
145 containsClose = true;
148 @Override
149 public void visitReferenceExpression(
150 PsiReferenceExpression referenceExpression) {
151 // check if resource is closed in IOUtils.silentClose() like method
152 super.visitReferenceExpression(referenceExpression);
153 if (containsClose) {
154 return;
156 final String text = referenceExpression.getText();
157 if (text == null || !text.equals(resourceName)) {
158 return;
160 final PsiElement parent = referenceExpression.getParent();
161 if (!(parent instanceof PsiExpressionList)) {
162 return;
164 final PsiExpressionList argumentList = (PsiExpressionList) parent;
165 final PsiExpression[] arguments = argumentList.getExpressions();
166 if (arguments.length != 1) {
167 return;
169 final PsiElement grandParent = parent.getParent();
170 if (!(grandParent instanceof PsiMethodCallExpression)) {
171 return;
173 final PsiMethodCallExpression methodCallExpression =
174 (PsiMethodCallExpression) grandParent;
175 final PsiElement target = referenceExpression.resolve();
176 if (target == null || !target.equals(resource)) {
177 return;
179 final PsiMethod method = methodCallExpression.resolveMethod();
180 if (method == null) {
181 return;
183 final PsiCodeBlock codeBlock = method.getBody();
184 if (codeBlock == null) {
185 return;
187 final PsiParameterList parameterList = method.getParameterList();
188 final PsiParameter[] parameters = parameterList.getParameters();
189 if (parameters.length != 1) {
190 return;
192 final PsiParameter parameter = parameters[0];
193 final PsiStatement[] statements = codeBlock.getStatements();
194 for (PsiStatement statement : statements) {
195 if (!(statement instanceof PsiTryStatement)) {
196 continue;
198 final PsiTryStatement tryStatement = (PsiTryStatement) statement;
199 final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
200 if (tryBlock == null) {
201 return;
203 final PsiStatement[] innerStatements = tryBlock.getStatements();
204 for (PsiStatement innerStatement : innerStatements) {
205 if (!(innerStatement instanceof PsiExpressionStatement)) {
206 continue;
208 final PsiExpressionStatement expressionStatement =
209 (PsiExpressionStatement) innerStatement;
210 final PsiExpression expression =
211 expressionStatement.getExpression();
212 if (!(expression instanceof PsiMethodCallExpression)) {
213 continue;
215 final PsiMethodCallExpression potentialCloseExpression =
216 (PsiMethodCallExpression) expression;
217 if (isResourceClose(potentialCloseExpression, parameter)) {
218 containsClose = true;
219 return;
225 private static boolean isResourceClose(PsiMethodCallExpression call,
226 PsiVariable resource) {
227 final PsiReferenceExpression methodExpression =
228 call.getMethodExpression();
229 final String methodName = methodExpression.getReferenceName();
230 if (!HardcodedMethodConstants.CLOSE.equals(methodName)) {
231 return false;
233 final PsiExpression qualifier =
234 methodExpression.getQualifierExpression();
235 if (!(qualifier instanceof PsiReferenceExpression)) {
236 return false;
238 final PsiReference reference = (PsiReference) qualifier;
239 final PsiElement referent = reference.resolve();
240 return referent != null && referent.equals(resource);
243 public boolean containsClose() {
244 return containsClose;
248 private static class EscapeVisitor extends JavaRecursiveElementVisitor{
250 private final PsiVariable boundVariable;
251 private boolean escaped = false;
253 public EscapeVisitor(PsiVariable boundVariable){
254 this.boundVariable = boundVariable;
257 @Override public void visitAnonymousClass(PsiAnonymousClass aClass){}
259 @Override
260 public void visitElement(PsiElement element){
261 if(escaped){
262 return;
264 super.visitElement(element);
267 @Override public void visitReturnStatement(
268 PsiReturnStatement statement){
269 PsiExpression value = statement.getReturnValue();
270 value = PsiUtil.deparenthesizeExpression(value);
271 if (value instanceof PsiReferenceExpression){
272 final PsiReferenceExpression referenceExpression =
273 (PsiReferenceExpression) value;
274 final PsiElement target = referenceExpression.resolve();
275 if(target == boundVariable){
276 escaped = true;
281 public boolean isEscaped(){
282 return escaped;