2 * Copyright 2000-2009 JetBrains s.r.o.
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
.intellij
.refactoring
.inline
;
18 import com
.intellij
.codeInsight
.ExceptionUtil
;
19 import com
.intellij
.codeInspection
.sameParameterValue
.SameParameterValueInspection
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.openapi
.util
.Key
;
22 import com
.intellij
.openapi
.util
.Ref
;
23 import com
.intellij
.psi
.*;
24 import com
.intellij
.psi
.controlFlow
.DefUseUtil
;
25 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
26 import com
.intellij
.psi
.util
.PsiTreeUtil
;
27 import com
.intellij
.psi
.util
.PsiUtil
;
28 import com
.intellij
.refactoring
.BaseRefactoringProcessor
;
29 import com
.intellij
.refactoring
.util
.InlineUtil
;
30 import com
.intellij
.refactoring
.util
.RefactoringUIUtil
;
31 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
32 import com
.intellij
.usageView
.UsageInfo
;
33 import com
.intellij
.usageView
.UsageViewDescriptor
;
34 import com
.intellij
.usageView
.UsageViewUtil
;
35 import com
.intellij
.util
.containers
.MultiMap
;
36 import org
.jetbrains
.annotations
.NotNull
;
37 import org
.jetbrains
.annotations
.Nullable
;
44 public class InlineParameterExpressionProcessor
extends BaseRefactoringProcessor
{
45 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.inline.InlineParameterExpressionProcessor");
46 public static final Key
<Boolean
> CREATE_LOCAL_FOR_TESTS
= Key
.create("CREATE_INLINE_PARAMETER_LOCAL_FOR_TESTS");
48 private final PsiCallExpression myMethodCall
;
49 private final PsiMethod myMethod
;
50 private final PsiParameter myParameter
;
51 private PsiExpression myInitializer
;
52 private final boolean mySameClass
;
53 private final PsiMethod myCallingMethod
;
54 private boolean myCreateLocal
;
56 public InlineParameterExpressionProcessor(final PsiCallExpression methodCall
,
57 final PsiMethod method
,
58 final PsiParameter parameter
,
59 final PsiExpression initializer
,
60 boolean createLocal
) {
61 super(method
.getProject());
62 myMethodCall
= methodCall
;
64 myParameter
= parameter
;
65 myInitializer
= initializer
;
66 myCreateLocal
= createLocal
;
68 PsiClass callingClass
= PsiTreeUtil
.getParentOfType(methodCall
, PsiClass
.class);
69 mySameClass
= (callingClass
== myMethod
.getContainingClass());
70 myCallingMethod
= PsiTreeUtil
.getParentOfType(myMethodCall
, PsiMethod
.class);
74 protected String
getCommandName() {
75 return InlineParameterHandler
.REFACTORING_NAME
;
79 protected void refreshElements(PsiElement
[] elements
) {
83 protected UsageViewDescriptor
createUsageViewDescriptor(UsageInfo
[] usages
) {
84 return new InlineViewDescriptor(myParameter
);
89 protected UsageInfo
[] findUsages() {
91 int parameterIndex
= myMethod
.getParameterList().getParameterIndex(myParameter
);
92 final Map
<PsiVariable
, PsiElement
> localToParamRef
= new HashMap
<PsiVariable
, PsiElement
>();
93 final PsiExpression
[] arguments
= myMethodCall
.getArgumentList().getExpressions();
94 for (int i
= 0; i
< arguments
.length
; i
++) {
95 if (i
!= parameterIndex
&& arguments
[i
] instanceof PsiReferenceExpression
) {
96 final PsiReferenceExpression referenceExpression
= (PsiReferenceExpression
)arguments
[i
];
97 final PsiElement element
= referenceExpression
.resolve();
98 if (element
instanceof PsiLocalVariable
|| element
instanceof PsiParameter
) {
99 final PsiParameter param
= myMethod
.getParameterList().getParameters()[i
];
100 final PsiExpression paramRef
=
101 JavaPsiFacade
.getInstance(myMethod
.getProject()).getElementFactory().createExpressionFromText(param
.getName(), myMethod
);
102 localToParamRef
.put((PsiVariable
)element
, paramRef
);
107 final List
<UsageInfo
> result
= new ArrayList
<UsageInfo
>();
108 myInitializer
.accept(new JavaRecursiveElementVisitor() {
110 public void visitReferenceExpression(final PsiReferenceExpression expression
) {
111 super.visitReferenceExpression(expression
);
112 final PsiElement element
= expression
.resolve();
113 if (element
instanceof PsiLocalVariable
) {
114 final PsiLocalVariable localVariable
= (PsiLocalVariable
)element
;
115 final PsiElement
[] elements
= DefUseUtil
.getDefs(myCallingMethod
.getBody(), localVariable
, expression
);
116 if (elements
.length
== 1) {
117 PsiExpression localInitializer
= null;
118 if (elements
[0] instanceof PsiLocalVariable
) {
119 localInitializer
= ((PsiLocalVariable
)elements
[0]).getInitializer();
121 else if (elements
[0] instanceof PsiAssignmentExpression
) {
122 localInitializer
= ((PsiAssignmentExpression
)elements
[0]).getRExpression();
124 else if (elements
[0] instanceof PsiReferenceExpression
) {
125 final PsiReferenceExpression refElement
= (PsiReferenceExpression
)elements
[0];
126 final PsiElement parent
= refElement
.getParent();
127 if (parent
instanceof PsiAssignmentExpression
&& ((PsiAssignmentExpression
)parent
).getLExpression() == refElement
) {
128 localInitializer
= ((PsiAssignmentExpression
)parent
).getRExpression();
131 if (localInitializer
!= null) {
132 final PsiElement replacement
;
133 if (localToParamRef
.containsKey(localVariable
)) {
134 replacement
= localToParamRef
.get(localVariable
);
137 replacement
= replaceArgs(localToParamRef
, localInitializer
.copy());
139 result
.add(new LocalReplacementUsageInfo(expression
, replacement
));
146 if (!myCreateLocal
) {
147 for (PsiReference ref
: ReferencesSearch
.search(myParameter
).findAll()) {
148 result
.add(new UsageInfo(ref
));
152 final UsageInfo
[] usageInfos
= result
.toArray(new UsageInfo
[result
.size()]);
153 return UsageViewUtil
.removeDuplicatedUsages(usageInfos
);
156 private static PsiElement
replaceArgs(final Map
<PsiVariable
, PsiElement
> elementsToReplace
, PsiElement expression
) {
157 final Map
<PsiElement
, PsiElement
> replacements
= new HashMap
<PsiElement
, PsiElement
>();
158 expression
.accept(new JavaRecursiveElementVisitor() {
160 public void visitReferenceExpression(PsiReferenceExpression referenceExpression
) {
161 super.visitReferenceExpression(referenceExpression
);
162 final PsiElement resolved
= referenceExpression
.resolve();
163 if (resolved
instanceof PsiVariable
) {
164 final PsiVariable variable
= (PsiVariable
)resolved
;
165 final PsiElement replacement
= elementsToReplace
.get(variable
);
166 if (replacement
!= null) {
167 replacements
.put(referenceExpression
, replacement
);
172 return RefactoringUtil
.replaceElementsWithMap(expression
, replacements
);
176 protected boolean preprocessUsages(Ref
<UsageInfo
[]> refUsages
) {
177 final MultiMap
<PsiElement
, String
> conflicts
= new MultiMap
<PsiElement
, String
>();
178 myInitializer
.accept(new JavaRecursiveElementWalkingVisitor() {
180 public void visitReferenceExpression(final PsiReferenceExpression expression
) {
181 super.visitReferenceExpression(expression
);
182 final PsiElement element
= expression
.resolve();
183 if (element
instanceof PsiMember
&& !((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
184 if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
185 conflicts
.putValue(expression
, "Parameter initializer depends on " + RefactoringUIUtil
.getDescription(element
, false) + " which is not available inside the static method");
188 if (element
instanceof PsiMethod
|| element
instanceof PsiField
) {
189 if (!mySameClass
&& !((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
190 conflicts
.putValue(expression
, "Parameter initializer depend on non static member from some other class");
191 } else if (!PsiUtil
.isAccessible((PsiMember
)element
, myMethod
, null)) {
192 conflicts
.putValue(expression
, "Parameter initializer depends on value which is not available inside method");
194 } else if (element
instanceof PsiParameter
) {
195 conflicts
.putValue(expression
, "Parameter initializer depends on callers parameter");
200 public void visitThisExpression(PsiThisExpression thisExpression
) {
201 super.visitThisExpression(thisExpression
);
202 final PsiJavaCodeReferenceElement qualifier
= thisExpression
.getQualifier();
203 PsiElement containingClass
;
204 if (qualifier
!= null) {
205 containingClass
= qualifier
.resolve();
208 containingClass
= PsiTreeUtil
.getParentOfType(myMethodCall
, PsiClass
.class);
210 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
211 LOG
.assertTrue(methodContainingClass
!= null);
212 if (!PsiTreeUtil
.isAncestor(containingClass
, methodContainingClass
, false)) {
213 conflicts
.putValue(thisExpression
,
214 "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
215 } else if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
216 conflicts
.putValue(thisExpression
, "Parameter initializer depends on this which is not available inside the static method");
221 public void visitReferenceElement(PsiJavaCodeReferenceElement reference
) {
222 super.visitReferenceElement(reference
);
223 if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
224 final PsiElement resolved
= reference
.resolve();
225 if (resolved
instanceof PsiClass
&& !((PsiClass
)resolved
).hasModifierProperty(PsiModifier
.STATIC
)) {
226 conflicts
.putValue(reference
, "Parameter initializer depends on non static class which is not available inside static method");
232 public void visitNewExpression(PsiNewExpression expression
) {
233 super.visitNewExpression(expression
);
234 final PsiJavaCodeReferenceElement reference
= expression
.getClassOrAnonymousClassReference();
235 if (reference
!= null) {
236 final PsiElement resolved
= reference
.resolve();
237 if (resolved
instanceof PsiClass
) {
238 final PsiClass refClass
= (PsiClass
)resolved
;
239 final String classUnavailableMessage
= "Parameter initializer depends on " +
240 RefactoringUIUtil
.getDescription(refClass
, true) +
241 " which is not available inside method and cannot be inlined";
242 if (!PsiUtil
.isAccessible(refClass
, myMethod
, null)) {
243 conflicts
.putValue(expression
, classUnavailableMessage
);
246 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
247 LOG
.assertTrue(methodContainingClass
!= null);
248 if (!PsiTreeUtil
.isAncestor(myMethod
, refClass
, false)) {
249 PsiElement parent
= refClass
;
250 while ((parent
= parent
.getParent()) instanceof PsiClass
) {
251 if (!PsiUtil
.isAccessible((PsiClass
)parent
, myMethod
, null)) {
255 if (!(parent
instanceof PsiFile
)) {
256 conflicts
.putValue(expression
, classUnavailableMessage
);
265 final UsageInfo
[] usages
= refUsages
.get();
266 final Set
<PsiVariable
> vars
= new HashSet
<PsiVariable
>();
267 for (UsageInfo usageInfo
: usages
) {
268 if (usageInfo
instanceof LocalReplacementUsageInfo
) {
269 final PsiVariable var
= ((LocalReplacementUsageInfo
)usageInfo
).getVariable();
275 for (PsiVariable var
: vars
) {
276 for (PsiReference ref
: ReferencesSearch
.search(var
)) {
277 final PsiElement element
= ref
.getElement();
278 if (element
instanceof PsiExpression
&& isAccessedForWriting((PsiExpression
)element
)) {
279 conflicts
.putValue(element
, "Parameter initializer depends on value which is not available inside method and cannot be inlined");
284 return showConflicts(conflicts
);
287 private static boolean isAccessedForWriting (PsiExpression expr
) {
288 while (expr
.getParent() instanceof PsiArrayAccessExpression
) {
289 expr
= (PsiExpression
)expr
.getParent();
291 return PsiUtil
.isAccessedForWriting(expr
);
295 protected void performRefactoring(UsageInfo
[] usages
) {
296 final List
<PsiClassType
> thrownExceptions
= ExceptionUtil
.getThrownCheckedExceptions(new PsiElement
[]{myInitializer
});
297 final Set
<PsiVariable
> varsUsedInInitializer
= new HashSet
<PsiVariable
>();
298 final Set
<PsiJavaCodeReferenceElement
> paramRefsToInline
= new HashSet
<PsiJavaCodeReferenceElement
>();
299 final Map
<PsiElement
, PsiElement
> replacements
= new HashMap
<PsiElement
, PsiElement
>();
300 for (UsageInfo usage
: usages
) {
301 if (usage
instanceof LocalReplacementUsageInfo
) {
302 final LocalReplacementUsageInfo replacementUsageInfo
= (LocalReplacementUsageInfo
)usage
;
303 final PsiElement element
= replacementUsageInfo
.getElement();
304 final PsiElement replacement
= replacementUsageInfo
.getReplacement();
305 if (element
!= null && replacement
!= null) {
306 replacements
.put(element
, replacement
);
308 varsUsedInInitializer
.add(replacementUsageInfo
.getVariable());
311 LOG
.assertTrue(!myCreateLocal
);
312 paramRefsToInline
.add((PsiJavaCodeReferenceElement
)usage
.getElement());
315 myInitializer
= (PsiExpression
)RefactoringUtil
.replaceElementsWithMap(myInitializer
, replacements
);
318 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(myMethod
.getProject()).getElementFactory();
319 PsiDeclarationStatement localDeclaration
=
320 factory
.createVariableDeclarationStatement(myParameter
.getName(), myParameter
.getType(), myInitializer
);
321 final PsiLocalVariable declaredVar
= (PsiLocalVariable
)localDeclaration
.getDeclaredElements()[0];
322 PsiUtil
.setModifierProperty(declaredVar
, PsiModifier
.FINAL
, myParameter
.hasModifierProperty(PsiModifier
.FINAL
));
323 final PsiExpression localVarInitializer
=
324 InlineUtil
.inlineVariable(myParameter
, myInitializer
, (PsiReferenceExpression
)factory
.createExpressionFromText(myParameter
.getName(), myMethod
));
325 final PsiExpression initializer
= declaredVar
.getInitializer();
326 LOG
.assertTrue(initializer
!= null);
327 initializer
.replace(localVarInitializer
);
328 final PsiCodeBlock body
= myMethod
.getBody();
330 body
.addAfter(localDeclaration
, body
.getLBrace());
333 for (PsiJavaCodeReferenceElement paramRef
: paramRefsToInline
) {
334 InlineUtil
.inlineVariable(myParameter
, myInitializer
, paramRef
);
338 //delete var if it becomes unused
339 for (PsiVariable variable
: varsUsedInInitializer
) {
340 if (variable
!= null && variable
.isValid()) {
341 if (ReferencesSearch
.search(variable
).findFirst() == null) {
347 SameParameterValueInspection
.InlineParameterValueFix
.removeParameter(myMethod
, myParameter
);
349 if (!thrownExceptions
.isEmpty()) {
350 for (PsiClassType exception
: thrownExceptions
) {
351 PsiClass exceptionClass
= exception
.resolve();
352 if (exceptionClass
!= null) {
353 PsiUtil
.addException(myMethod
, exceptionClass
);
359 private static class LocalReplacementUsageInfo
extends UsageInfo
{
360 private final PsiElement myReplacement
;
361 private final PsiVariable myVariable
;
363 public LocalReplacementUsageInfo(@NotNull PsiReference element
, @NotNull PsiElement replacement
) {
365 final PsiElement resolved
= element
.resolve();
366 myVariable
= resolved
instanceof PsiVariable ?
(PsiVariable
)resolved
: null;
367 myReplacement
= replacement
;
371 public PsiElement
getReplacement() {
372 return myReplacement
.isValid() ? myReplacement
: null;
376 public PsiVariable
getVariable() {
377 return myVariable
!= null && myVariable
.isValid() ? myVariable
: null;