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 if (localInitializer
!= null) {
125 final PsiElement replacement
;
126 if (localToParamRef
.containsKey(localVariable
)) {
127 replacement
= localToParamRef
.get(localVariable
);
130 replacement
= replaceArgs(localToParamRef
, localInitializer
.copy());
132 result
.add(new LocalReplacementUsageInfo(expression
, replacement
));
139 if (!myCreateLocal
) {
140 for (PsiReference ref
: ReferencesSearch
.search(myParameter
).findAll()) {
141 result
.add(new UsageInfo(ref
));
145 final UsageInfo
[] usageInfos
= result
.toArray(new UsageInfo
[result
.size()]);
146 return UsageViewUtil
.removeDuplicatedUsages(usageInfos
);
149 private static PsiElement
replaceArgs(final Map
<PsiVariable
, PsiElement
> elementsToReplace
, PsiElement expression
) {
150 final Map
<PsiElement
, PsiElement
> replacements
= new HashMap
<PsiElement
, PsiElement
>();
151 expression
.accept(new JavaRecursiveElementVisitor() {
153 public void visitReferenceExpression(PsiReferenceExpression referenceExpression
) {
154 super.visitReferenceExpression(referenceExpression
);
155 final PsiElement resolved
= referenceExpression
.resolve();
156 if (resolved
instanceof PsiVariable
) {
157 final PsiVariable variable
= (PsiVariable
)resolved
;
158 final PsiElement replacement
= elementsToReplace
.get(variable
);
159 if (replacement
!= null) {
160 replacements
.put(referenceExpression
, replacement
);
165 return RefactoringUtil
.replaceElementsWithMap(expression
, replacements
);
169 protected boolean preprocessUsages(Ref
<UsageInfo
[]> refUsages
) {
170 final MultiMap
<PsiElement
, String
> conflicts
= new MultiMap
<PsiElement
, String
>();
171 myInitializer
.accept(new JavaRecursiveElementWalkingVisitor() {
173 public void visitReferenceExpression(final PsiReferenceExpression expression
) {
174 super.visitReferenceExpression(expression
);
175 final PsiElement element
= expression
.resolve();
176 if (element
instanceof PsiMethod
|| element
instanceof PsiField
) {
177 if (!mySameClass
&& !((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
178 conflicts
.putValue(expression
, "Parameter initializer depend on non static member from some other class");
180 } else if (element
instanceof PsiParameter
) {
181 conflicts
.putValue(expression
, "Parameter initializer depends on callers parameter");
186 public void visitThisExpression(PsiThisExpression thisExpression
) {
187 super.visitThisExpression(thisExpression
);
188 final PsiJavaCodeReferenceElement qualifier
= thisExpression
.getQualifier();
189 PsiElement containingClass
;
190 if (qualifier
!= null) {
191 containingClass
= qualifier
.resolve();
194 containingClass
= PsiTreeUtil
.getParentOfType(myMethodCall
, PsiClass
.class);
196 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
197 LOG
.assertTrue(methodContainingClass
!= null);
198 if (!PsiTreeUtil
.isAncestor(containingClass
, methodContainingClass
, false)) {
199 conflicts
.putValue(thisExpression
,
200 "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
205 public void visitNewExpression(PsiNewExpression expression
) {
206 super.visitNewExpression(expression
);
207 final PsiJavaCodeReferenceElement reference
= expression
.getClassOrAnonymousClassReference();
208 if (reference
!= null) {
209 final PsiElement resolved
= reference
.resolve();
210 if (resolved
instanceof PsiClass
) {
211 final PsiClass refClass
= (PsiClass
)resolved
;
212 final String classUnavailableMessage
= "Parameter initializer depends on " +
213 RefactoringUIUtil
.getDescription(refClass
, true) +
214 " which is not available inside method and cannot be inlined";
215 if (!PsiUtil
.isAccessible(refClass
, myMethod
, null)) {
216 conflicts
.putValue(expression
, classUnavailableMessage
);
219 final PsiClass methodContainingClass
= myMethod
.getContainingClass();
220 LOG
.assertTrue(methodContainingClass
!= null);
221 if (!PsiTreeUtil
.isAncestor(myMethod
, refClass
, false)) {
222 PsiElement parent
= refClass
;
223 while ((parent
= parent
.getParent()) instanceof PsiClass
) {
224 if (!PsiUtil
.isAccessible((PsiClass
)parent
, myMethod
, null)) {
228 if (!(parent
instanceof PsiFile
)) {
229 conflicts
.putValue(expression
, classUnavailableMessage
);
238 final UsageInfo
[] usages
= refUsages
.get();
239 final Set
<PsiVariable
> vars
= new HashSet
<PsiVariable
>();
240 for (UsageInfo usageInfo
: usages
) {
241 if (usageInfo
instanceof LocalReplacementUsageInfo
) {
242 final PsiVariable var
= ((LocalReplacementUsageInfo
)usageInfo
).getVariable();
248 for (PsiVariable var
: vars
) {
249 for (PsiReference ref
: ReferencesSearch
.search(var
)) {
250 final PsiElement element
= ref
.getElement();
251 if (element
instanceof PsiExpression
&& isAccessedForWriting((PsiExpression
)element
)) {
252 conflicts
.putValue(element
, "Parameter initializer depends on value which is not available inside method and cannot be inlined");
257 return showConflicts(conflicts
);
260 private static boolean isAccessedForWriting (PsiExpression expr
) {
261 while (expr
.getParent() instanceof PsiArrayAccessExpression
) {
262 expr
= (PsiExpression
)expr
.getParent();
264 return PsiUtil
.isAccessedForWriting(expr
);
268 protected void performRefactoring(UsageInfo
[] usages
) {
269 final List
<PsiClassType
> thrownExceptions
= ExceptionUtil
.getThrownCheckedExceptions(new PsiElement
[]{myInitializer
});
270 final Set
<PsiVariable
> varsUsedInInitializer
= new HashSet
<PsiVariable
>();
271 final Set
<PsiJavaCodeReferenceElement
> paramRefsToInline
= new HashSet
<PsiJavaCodeReferenceElement
>();
272 final Map
<PsiElement
, PsiElement
> replacements
= new HashMap
<PsiElement
, PsiElement
>();
273 for (UsageInfo usage
: usages
) {
274 if (usage
instanceof LocalReplacementUsageInfo
) {
275 final LocalReplacementUsageInfo replacementUsageInfo
= (LocalReplacementUsageInfo
)usage
;
276 final PsiElement element
= replacementUsageInfo
.getElement();
277 final PsiElement replacement
= replacementUsageInfo
.getReplacement();
278 if (element
!= null && replacement
!= null) {
279 replacements
.put(element
, replacement
);
281 varsUsedInInitializer
.add(replacementUsageInfo
.getVariable());
284 LOG
.assertTrue(!myCreateLocal
);
285 paramRefsToInline
.add((PsiJavaCodeReferenceElement
)usage
.getElement());
288 myInitializer
= (PsiExpression
)RefactoringUtil
.replaceElementsWithMap(myInitializer
, replacements
);
291 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(myMethod
.getProject()).getElementFactory();
292 PsiDeclarationStatement localDeclaration
=
293 factory
.createVariableDeclarationStatement(myParameter
.getName(), myParameter
.getType(), myInitializer
);
294 final PsiLocalVariable declaredVar
= (PsiLocalVariable
)localDeclaration
.getDeclaredElements()[0];
295 PsiUtil
.setModifierProperty(declaredVar
, PsiModifier
.FINAL
, myParameter
.hasModifierProperty(PsiModifier
.FINAL
));
296 final PsiCodeBlock body
= myMethod
.getBody();
298 body
.addAfter(localDeclaration
, body
.getLBrace());
301 for (PsiJavaCodeReferenceElement paramRef
: paramRefsToInline
) {
302 InlineUtil
.inlineVariable(myParameter
, myInitializer
, paramRef
);
306 //delete var if it becomes unused
307 for (PsiVariable variable
: varsUsedInInitializer
) {
308 if (variable
!= null && variable
.isValid()) {
309 if (ReferencesSearch
.search(variable
).findFirst() == null) {
315 SameParameterValueInspection
.InlineParameterValueFix
.removeParameter(myMethod
, myParameter
);
317 if (!thrownExceptions
.isEmpty()) {
318 for (PsiClassType exception
: thrownExceptions
) {
319 PsiClass exceptionClass
= exception
.resolve();
320 if (exceptionClass
!= null) {
321 PsiUtil
.addException(myMethod
, exceptionClass
);
327 private static class LocalReplacementUsageInfo
extends UsageInfo
{
328 private final PsiElement myReplacement
;
329 private final PsiVariable myVariable
;
331 public LocalReplacementUsageInfo(@NotNull PsiReference element
, @NotNull PsiElement replacement
) {
333 final PsiElement resolved
= element
.resolve();
334 myVariable
= resolved
instanceof PsiVariable ?
(PsiVariable
)resolved
: null;
335 myReplacement
= replacement
;
339 public PsiElement
getReplacement() {
340 return myReplacement
.isValid() ? myReplacement
: null;
344 public PsiVariable
getVariable() {
345 return myVariable
!= null && myVariable
.isValid() ? myVariable
: null;