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 final UsageInfo
[] usages
= refUsages
.get();
179 final InaccessibleExpressionsDetector detector
= new InaccessibleExpressionsDetector(conflicts
);
180 myInitializer
.accept(detector
);
181 for (UsageInfo usage
: usages
) {
182 if (usage
instanceof LocalReplacementUsageInfo
) {
183 final PsiElement replacement
= ((LocalReplacementUsageInfo
)usage
).getReplacement();
184 if (replacement
!= null) {
185 replacement
.accept(detector
);
190 final Set
<PsiVariable
> vars
= new HashSet
<PsiVariable
>();
191 for (UsageInfo usageInfo
: usages
) {
192 if (usageInfo
instanceof LocalReplacementUsageInfo
) {
193 final PsiVariable var
= ((LocalReplacementUsageInfo
)usageInfo
).getVariable();
199 for (PsiVariable var
: vars
) {
200 for (PsiReference ref
: ReferencesSearch
.search(var
)) {
201 final PsiElement element
= ref
.getElement();
202 if (element
instanceof PsiExpression
&& isAccessedForWriting((PsiExpression
)element
)) {
203 conflicts
.putValue(element
, "Parameter initializer depends on value which is not available inside method and cannot be inlined");
208 return showConflicts(conflicts
);
211 private static boolean isAccessedForWriting (PsiExpression expr
) {
212 while (expr
.getParent() instanceof PsiArrayAccessExpression
) {
213 expr
= (PsiExpression
)expr
.getParent();
215 return PsiUtil
.isAccessedForWriting(expr
);
219 protected void performRefactoring(UsageInfo
[] usages
) {
220 final List
<PsiClassType
> thrownExceptions
= ExceptionUtil
.getThrownCheckedExceptions(new PsiElement
[]{myInitializer
});
221 final Set
<PsiVariable
> varsUsedInInitializer
= new HashSet
<PsiVariable
>();
222 final Set
<PsiJavaCodeReferenceElement
> paramRefsToInline
= new HashSet
<PsiJavaCodeReferenceElement
>();
223 final Map
<PsiElement
, PsiElement
> replacements
= new HashMap
<PsiElement
, PsiElement
>();
224 for (UsageInfo usage
: usages
) {
225 if (usage
instanceof LocalReplacementUsageInfo
) {
226 final LocalReplacementUsageInfo replacementUsageInfo
= (LocalReplacementUsageInfo
)usage
;
227 final PsiElement element
= replacementUsageInfo
.getElement();
228 final PsiElement replacement
= replacementUsageInfo
.getReplacement();
229 if (element
!= null && replacement
!= null) {
230 replacements
.put(element
, replacement
);
232 varsUsedInInitializer
.add(replacementUsageInfo
.getVariable());
235 LOG
.assertTrue(!myCreateLocal
);
236 paramRefsToInline
.add((PsiJavaCodeReferenceElement
)usage
.getElement());
239 myInitializer
= (PsiExpression
)RefactoringUtil
.replaceElementsWithMap(myInitializer
, replacements
);
242 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(myMethod
.getProject()).getElementFactory();
243 PsiDeclarationStatement localDeclaration
=
244 factory
.createVariableDeclarationStatement(myParameter
.getName(), myParameter
.getType(), myInitializer
);
245 final PsiLocalVariable declaredVar
= (PsiLocalVariable
)localDeclaration
.getDeclaredElements()[0];
246 PsiUtil
.setModifierProperty(declaredVar
, PsiModifier
.FINAL
, myParameter
.hasModifierProperty(PsiModifier
.FINAL
));
247 final PsiExpression localVarInitializer
=
248 InlineUtil
.inlineVariable(myParameter
, myInitializer
, (PsiReferenceExpression
)factory
.createExpressionFromText(myParameter
.getName(), myMethod
));
249 final PsiExpression initializer
= declaredVar
.getInitializer();
250 LOG
.assertTrue(initializer
!= null);
251 initializer
.replace(localVarInitializer
);
252 final PsiCodeBlock body
= myMethod
.getBody();
254 body
.addAfter(localDeclaration
, body
.getLBrace());
257 for (PsiJavaCodeReferenceElement paramRef
: paramRefsToInline
) {
258 InlineUtil
.inlineVariable(myParameter
, myInitializer
, paramRef
);
262 //delete var if it becomes unused
263 for (PsiVariable variable
: varsUsedInInitializer
) {
264 if (variable
!= null && variable
.isValid()) {
265 if (ReferencesSearch
.search(variable
).findFirst() == null) {
271 SameParameterValueInspection
.InlineParameterValueFix
.removeParameter(myMethod
, myParameter
);
273 if (!thrownExceptions
.isEmpty()) {
274 for (PsiClassType exception
: thrownExceptions
) {
275 PsiClass exceptionClass
= exception
.resolve();
276 if (exceptionClass
!= null) {
277 PsiUtil
.addException(myMethod
, exceptionClass
);
283 private static class LocalReplacementUsageInfo
extends UsageInfo
{
284 private final PsiElement myReplacement
;
285 private final PsiVariable myVariable
;
287 public LocalReplacementUsageInfo(@NotNull PsiReference element
, @NotNull PsiElement replacement
) {
289 final PsiElement resolved
= element
.resolve();
290 myVariable
= resolved
instanceof PsiVariable ?
(PsiVariable
)resolved
: null;
291 myReplacement
= replacement
;
295 public PsiElement
getReplacement() {
296 return myReplacement
.isValid() ? myReplacement
: null;
300 public PsiVariable
getVariable() {
301 return myVariable
!= null && myVariable
.isValid() ? myVariable
: null;
305 private class InaccessibleExpressionsDetector
extends JavaRecursiveElementWalkingVisitor
{
306 private final MultiMap
<PsiElement
, String
> myConflicts
;
308 public InaccessibleExpressionsDetector(MultiMap
<PsiElement
, String
> conflicts
) {
309 myConflicts
= conflicts
;
313 public void visitReferenceExpression(final PsiReferenceExpression expression
) {
314 super.visitReferenceExpression(expression
);
315 final PsiElement element
= expression
.resolve();
316 if (element
instanceof PsiMember
&& !((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
317 if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
318 myConflicts
.putValue(expression
, "Parameter initializer depends on " + RefactoringUIUtil
.getDescription(element
, false) + " which is not available inside the static method");
321 if (element
instanceof PsiMethod
|| element
instanceof PsiField
) {
322 if (!mySameClass
&& !((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
323 myConflicts
.putValue(expression
, "Parameter initializer depend on non static member from some other class");
324 } else if (!PsiUtil
.isAccessible((PsiMember
)element
, myMethod
, null)) {
325 myConflicts
.putValue(expression
, "Parameter initializer depends on value which is not available inside method");
327 } else if (element
instanceof PsiParameter
) {
328 myConflicts
.putValue(expression
, "Parameter initializer depends on callers parameter");
333 public void visitThisExpression(PsiThisExpression thisExpression
) {
334 super.visitThisExpression(thisExpression
);
335 final PsiJavaCodeReferenceElement qualifier
= thisExpression
.getQualifier();
336 PsiElement containingClass
;
337 if (qualifier
!= null) {
338 containingClass
= qualifier
.resolve();
341 containingClass
= PsiTreeUtil
.getParentOfType(myMethodCall
, PsiClass
.class);
343 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
344 LOG
.assertTrue(methodContainingClass
!= null);
345 if (!PsiTreeUtil
.isAncestor(containingClass
, methodContainingClass
, false)) {
346 myConflicts
.putValue(thisExpression
,
347 "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
348 } else if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
349 myConflicts
.putValue(thisExpression
, "Parameter initializer depends on this which is not available inside the static method");
354 public void visitReferenceElement(PsiJavaCodeReferenceElement reference
) {
355 super.visitReferenceElement(reference
);
356 if (myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
357 final PsiElement resolved
= reference
.resolve();
358 if (resolved
instanceof PsiClass
&& !((PsiClass
)resolved
).hasModifierProperty(PsiModifier
.STATIC
)) {
359 myConflicts
.putValue(reference
, "Parameter initializer depends on non static class which is not available inside static method");
365 public void visitNewExpression(PsiNewExpression expression
) {
366 super.visitNewExpression(expression
);
367 final PsiJavaCodeReferenceElement reference
= expression
.getClassOrAnonymousClassReference();
368 if (reference
!= null) {
369 final PsiElement resolved
= reference
.resolve();
370 if (resolved
instanceof PsiClass
) {
371 final PsiClass refClass
= (PsiClass
)resolved
;
372 final String classUnavailableMessage
= "Parameter initializer depends on " +
373 RefactoringUIUtil
.getDescription(refClass
, true) +
374 " which is not available inside method and cannot be inlined";
375 if (!PsiUtil
.isAccessible(refClass
, myMethod
, null)) {
376 myConflicts
.putValue(expression
, classUnavailableMessage
);
379 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
380 LOG
.assertTrue(methodContainingClass
!= null);
381 if (!PsiTreeUtil
.isAncestor(myMethod
, refClass
, false)) {
382 PsiElement parent
= refClass
;
383 while ((parent
= parent
.getParent()) instanceof PsiClass
) {
384 if (!PsiUtil
.isAccessible((PsiClass
)parent
, myMethod
, null)) {
388 if (!(parent
instanceof PsiFile
)) {
389 myConflicts
.putValue(expression
, classUnavailableMessage
);