2 * Copyright 2003-2008 Dave Griffith, 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
.performance
;
18 import com
.intellij
.codeInspection
.ProblemDescriptor
;
19 import com
.intellij
.openapi
.project
.Project
;
20 import com
.intellij
.psi
.*;
21 import com
.intellij
.psi
.tree
.IElementType
;
22 import com
.intellij
.psi
.util
.PsiUtil
;
23 import com
.intellij
.util
.IncorrectOperationException
;
24 import com
.siyeh
.InspectionGadgetsBundle
;
25 import com
.siyeh
.ig
.BaseInspection
;
26 import com
.siyeh
.ig
.BaseInspectionVisitor
;
27 import com
.siyeh
.ig
.InspectionGadgetsFix
;
28 import com
.siyeh
.ig
.psiutils
.ExpressionUtils
;
29 import com
.siyeh
.ig
.psiutils
.ParenthesesUtils
;
30 import com
.siyeh
.ig
.psiutils
.SideEffectChecker
;
31 import com
.siyeh
.ig
.psiutils
.VariableAccessUtils
;
32 import org
.jetbrains
.annotations
.NonNls
;
33 import org
.jetbrains
.annotations
.NotNull
;
34 import org
.jetbrains
.annotations
.Nullable
;
36 public class ManualArrayCopyInspection
extends BaseInspection
{
40 public String
getDisplayName() {
41 return InspectionGadgetsBundle
.message(
42 "manual.array.copy.display.name");
46 public boolean isEnabledByDefault() {
52 protected String
buildErrorString(Object
... infos
) {
53 return InspectionGadgetsBundle
.message(
54 "manual.array.copy.problem.descriptor");
58 public BaseInspectionVisitor
buildVisitor() {
59 return new ManualArrayCopyVisitor();
63 public InspectionGadgetsFix
buildFix(Object
... infos
) {
64 return new ManualArrayCopyFix();
67 private static class ManualArrayCopyFix
extends InspectionGadgetsFix
{
70 public String
getName() {
71 return InspectionGadgetsBundle
.message(
72 "manual.array.copy.replace.quickfix");
76 public void doFix(Project project
, ProblemDescriptor descriptor
)
77 throws IncorrectOperationException
{
78 final PsiElement forElement
= descriptor
.getPsiElement();
79 final PsiForStatement forStatement
=
80 (PsiForStatement
)forElement
.getParent();
81 final String newExpression
= getSystemArrayCopyText(forStatement
);
82 if (newExpression
== null) {
85 replaceStatement(forStatement
, newExpression
);
89 private static String
getSystemArrayCopyText(
90 PsiForStatement forStatement
)
91 throws IncorrectOperationException
{
92 final PsiExpression condition
= forStatement
.getCondition();
93 final PsiBinaryExpression binaryExpression
=
94 (PsiBinaryExpression
)PsiUtil
.deparenthesizeExpression(
96 if (binaryExpression
== null) {
99 final PsiExpression limit
;
100 if (JavaTokenType
.LT
.equals(
101 binaryExpression
.getOperationTokenType())) {
102 limit
= binaryExpression
.getROperand();
104 limit
= binaryExpression
.getLOperand();
109 final PsiStatement initialization
=
110 forStatement
.getInitialization();
111 if (initialization
== null) {
114 if (!(initialization
instanceof PsiDeclarationStatement
)) {
117 final PsiDeclarationStatement declaration
=
118 (PsiDeclarationStatement
)initialization
;
119 if (declaration
.getDeclaredElements().length
!= 1) {
122 final PsiLocalVariable variable
= (PsiLocalVariable
)
123 declaration
.getDeclaredElements()[0];
124 final String lengthText
= getLengthText(limit
, variable
);
125 final PsiExpressionStatement body
= getBody(forStatement
);
129 final PsiAssignmentExpression assignment
=
130 (PsiAssignmentExpression
)body
.getExpression();
131 final PsiExpression lExpression
= assignment
.getLExpression();
132 final PsiArrayAccessExpression lhs
= (PsiArrayAccessExpression
)
133 PsiUtil
.deparenthesizeExpression(lExpression
);
137 final PsiExpression lArray
= lhs
.getArrayExpression();
138 final String toArrayText
= lArray
.getText();
139 final PsiExpression rExpression
= assignment
.getRExpression();
140 final PsiArrayAccessExpression rhs
= (PsiArrayAccessExpression
)
141 PsiUtil
.deparenthesizeExpression(rExpression
);
145 final PsiExpression rArray
= rhs
.getArrayExpression();
146 final String fromArrayText
= rArray
.getText();
147 final PsiExpression rhsIndexExpression
= rhs
.getIndexExpression();
148 final PsiExpression strippedRhsIndexExpression
=
149 PsiUtil
.deparenthesizeExpression(rhsIndexExpression
);
150 final String fromOffsetText
=
151 getOffsetText(strippedRhsIndexExpression
, variable
);
152 final PsiExpression lhsIndexExpression
= lhs
.getIndexExpression();
153 final PsiExpression strippedLhsIndexExpression
=
154 PsiUtil
.deparenthesizeExpression(lhsIndexExpression
);
155 final String toOffsetText
=
156 getOffsetText(strippedLhsIndexExpression
, variable
);
157 @NonNls final StringBuilder buffer
= new StringBuilder(60);
158 buffer
.append("System.arraycopy(");
159 buffer
.append(fromArrayText
);
161 buffer
.append(fromOffsetText
);
163 buffer
.append(toArrayText
);
165 buffer
.append(toOffsetText
);
167 buffer
.append(lengthText
);
169 return buffer
.toString();
173 private static String
getLengthText(PsiExpression expression
,
174 PsiVariable variable
) {
176 PsiUtil
.deparenthesizeExpression(expression
);
177 if (expression
== null) {
180 final PsiExpression initializer
= variable
.getInitializer();
181 final String expressionText
= expression
.getText();
182 if (initializer
== null) {
183 return expressionText
;
185 if (ExpressionUtils
.isZero(initializer
)) {
186 return expressionText
;
188 return expressionText
+ '-' + initializer
.getText();
192 private static String
getOffsetText(PsiExpression expression
,
193 PsiLocalVariable variable
)
194 throws IncorrectOperationException
{
195 if (expression
== null) {
198 final String expressionText
= expression
.getText();
199 final String variableName
= variable
.getName();
200 if (expressionText
.equals(variableName
)) {
201 final PsiExpression initialValue
= variable
.getInitializer();
202 if (initialValue
== null) {
205 return initialValue
.getText();
207 if (expression
instanceof PsiBinaryExpression
) {
208 final PsiBinaryExpression binaryExpression
=
209 (PsiBinaryExpression
)expression
;
210 final PsiExpression lhs
= binaryExpression
.getLOperand();
211 final String lhsText
= getOffsetText(lhs
, variable
);
212 final PsiExpression rhs
= binaryExpression
.getROperand();
213 final String rhsText
= getOffsetText(rhs
, variable
);
214 final PsiJavaToken sign
= binaryExpression
.getOperationSign();
215 final IElementType tokenType
= sign
.getTokenType();
216 if (lhsText
== null || lhsText
.equals("0")) {
217 if (tokenType
.equals(JavaTokenType
.MINUS
)) {
218 return '-' + rhsText
;
222 if (rhsText
== null || rhsText
.equals("0")) {
225 return collapseConstant(lhsText
+ sign
.getText() + rhsText
,
228 return collapseConstant(expression
.getText(), variable
);
231 private static String
collapseConstant(@NonNls String expressionText
,
233 throws IncorrectOperationException
{
234 final PsiManager manager
= context
.getManager();
235 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
236 final PsiConstantEvaluationHelper evaluationHelper
= JavaPsiFacade
.getInstance(manager
.getProject()).getConstantEvaluationHelper();
237 final PsiExpression fromOffsetExpression
=
238 factory
.createExpressionFromText(expressionText
, context
);
239 final Object fromOffsetConstant
=
240 evaluationHelper
.computeConstantExpression(
241 fromOffsetExpression
);
242 if (fromOffsetConstant
!= null) {
243 return fromOffsetConstant
.toString();
245 return expressionText
;
250 private static PsiExpressionStatement
getBody(
251 PsiForStatement forStatement
) {
252 PsiStatement body
= forStatement
.getBody();
253 while (body
instanceof PsiBlockStatement
) {
254 final PsiBlockStatement blockStatement
=
255 (PsiBlockStatement
)body
;
256 final PsiCodeBlock codeBlock
= blockStatement
.getCodeBlock();
257 final PsiStatement
[] statements
= codeBlock
.getStatements();
258 body
= statements
[0];
260 return (PsiExpressionStatement
)body
;
264 private static class ManualArrayCopyVisitor
extends BaseInspectionVisitor
{
266 @Override public void visitForStatement(@NotNull PsiForStatement statement
) {
267 super.visitForStatement(statement
);
268 final PsiStatement initialization
=
269 statement
.getInitialization();
270 if (!(initialization
instanceof PsiDeclarationStatement
)) {
273 final PsiDeclarationStatement declaration
=
274 (PsiDeclarationStatement
)initialization
;
275 if (declaration
.getDeclaredElements().length
!= 1) {
278 final PsiLocalVariable variable
= (PsiLocalVariable
)
279 declaration
.getDeclaredElements()[0];
280 final PsiExpression initialValue
= variable
.getInitializer();
281 if (initialValue
== null) {
284 final PsiExpression condition
= statement
.getCondition();
285 if (!ExpressionUtils
.isComparison(condition
, variable
)) {
288 final PsiStatement update
= statement
.getUpdate();
289 if (!VariableAccessUtils
.variableIsIncremented(variable
, update
)) {
292 final PsiStatement body
= statement
.getBody();
293 if (!bodyIsArrayCopy(body
, variable
)) {
296 registerStatementError(statement
);
299 private static boolean bodyIsArrayCopy(PsiStatement body
,
300 PsiLocalVariable variable
) {
301 if (body
instanceof PsiExpressionStatement
) {
302 final PsiExpressionStatement exp
=
303 (PsiExpressionStatement
)body
;
304 final PsiExpression expression
= exp
.getExpression();
305 return expressionIsArrayCopy(expression
, variable
);
306 } else if (body
instanceof PsiBlockStatement
) {
307 final PsiBlockStatement blockStatement
=
308 (PsiBlockStatement
)body
;
309 final PsiCodeBlock codeBlock
= blockStatement
.getCodeBlock();
310 final PsiStatement
[] statements
= codeBlock
.getStatements();
311 return statements
.length
== 1 &&
312 bodyIsArrayCopy(statements
[0], variable
);
317 private static boolean expressionIsArrayCopy(
318 @Nullable PsiExpression expression
,
319 @NotNull PsiVariable variable
) {
320 final PsiExpression strippedExpression
=
321 PsiUtil
.deparenthesizeExpression(expression
);
322 if (strippedExpression
== null) {
325 if (!(strippedExpression
instanceof PsiAssignmentExpression
)) {
328 final PsiAssignmentExpression assignment
=
329 (PsiAssignmentExpression
)strippedExpression
;
330 final PsiJavaToken sign
= assignment
.getOperationSign();
331 final IElementType tokenType
= sign
.getTokenType();
332 if (!tokenType
.equals(JavaTokenType
.EQ
)) {
335 final PsiExpression lhs
= assignment
.getLExpression();
336 if (SideEffectChecker
.mayHaveSideEffects(lhs
)) {
339 if (!ExpressionUtils
.isOffsetArrayAccess(lhs
, variable
)) {
342 final PsiExpression rhs
= assignment
.getRExpression();
346 if (SideEffectChecker
.mayHaveSideEffects(rhs
)) {
349 if (!areExpressionsCopyable(lhs
, rhs
)) {
352 final PsiType type
= lhs
.getType();
353 if (type
instanceof PsiPrimitiveType
) {
354 final PsiExpression strippedLhs
=
355 ParenthesesUtils
.stripParentheses(lhs
);
356 final PsiExpression strippedRhs
=
357 ParenthesesUtils
.stripParentheses(rhs
);
358 if (!areExpressionsCopyable(strippedLhs
, strippedRhs
)) {
362 return ExpressionUtils
.isOffsetArrayAccess(rhs
, variable
);
365 private static boolean areExpressionsCopyable(
366 @Nullable PsiExpression lhs
, @Nullable PsiExpression rhs
) {
367 if (lhs
== null || rhs
== null) {
370 final PsiType lhsType
= lhs
.getType();
371 if (lhsType
== null) {
374 final PsiType rhsType
= rhs
.getType();
375 if (rhsType
== null) {
378 if (lhsType
instanceof PsiPrimitiveType
) {
379 if (!lhsType
.equals(rhsType
)) {
383 if (!lhsType
.isAssignableFrom(rhsType
) ||
384 rhsType
instanceof PsiPrimitiveType
) {