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
.ChangeContextUtil
;
19 import com
.intellij
.history
.LocalHistory
;
20 import com
.intellij
.history
.LocalHistoryAction
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.editor
.Editor
;
23 import com
.intellij
.openapi
.editor
.LogicalPosition
;
24 import com
.intellij
.openapi
.project
.Project
;
25 import com
.intellij
.openapi
.util
.Key
;
26 import com
.intellij
.openapi
.util
.Ref
;
27 import com
.intellij
.psi
.*;
28 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
29 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
30 import com
.intellij
.psi
.codeStyle
.VariableKind
;
31 import com
.intellij
.psi
.controlFlow
.*;
32 import com
.intellij
.psi
.impl
.source
.codeStyle
.CodeEditUtil
;
33 import com
.intellij
.psi
.infos
.MethodCandidateInfo
;
34 import com
.intellij
.psi
.search
.GlobalSearchScope
;
35 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
36 import com
.intellij
.psi
.util
.InheritanceUtil
;
37 import com
.intellij
.psi
.util
.PsiTreeUtil
;
38 import com
.intellij
.psi
.util
.PsiTypesUtil
;
39 import com
.intellij
.psi
.util
.PsiUtil
;
40 import com
.intellij
.refactoring
.BaseRefactoringProcessor
;
41 import com
.intellij
.refactoring
.RefactoringBundle
;
42 import com
.intellij
.refactoring
.introduceParameter
.Util
;
43 import com
.intellij
.refactoring
.rename
.RenameJavaVariableProcessor
;
44 import com
.intellij
.refactoring
.ui
.ConflictsDialog
;
45 import com
.intellij
.refactoring
.util
.*;
46 import com
.intellij
.usageView
.UsageInfo
;
47 import com
.intellij
.usageView
.UsageViewDescriptor
;
48 import com
.intellij
.usageView
.UsageViewUtil
;
49 import com
.intellij
.util
.IncorrectOperationException
;
50 import com
.intellij
.util
.containers
.HashMap
;
51 import com
.intellij
.util
.containers
.MultiMap
;
52 import org
.jetbrains
.annotations
.NonNls
;
53 import org
.jetbrains
.annotations
.NotNull
;
54 import org
.jetbrains
.annotations
.Nullable
;
58 public class InlineMethodProcessor
extends BaseRefactoringProcessor
{
59 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.inline.InlineMethodProcessor");
61 private PsiMethod myMethod
;
62 private PsiJavaCodeReferenceElement myReference
;
63 private final Editor myEditor
;
64 private final boolean myInlineThisOnly
;
66 private final PsiManager myManager
;
67 private final PsiElementFactory myFactory
;
68 private final CodeStyleManager myCodeStyleManager
;
69 private final JavaCodeStyleManager myJavaCodeStyle
;
71 private PsiBlockStatement
[] myAddedBraces
;
72 private final String myDescriptiveName
;
73 private Map
<PsiField
, PsiClassInitializer
> myAddedClassInitializers
;
74 private PsiMethod myMethodCopy
;
76 public InlineMethodProcessor(@NotNull Project project
,
77 @NotNull PsiMethod method
,
78 @Nullable PsiJavaCodeReferenceElement reference
,
80 boolean isInlineThisOnly
) {
83 myReference
= reference
;
85 myInlineThisOnly
= isInlineThisOnly
;
87 myManager
= PsiManager
.getInstance(myProject
);
88 myFactory
= JavaPsiFacade
.getInstance(myManager
.getProject()).getElementFactory();
89 myCodeStyleManager
= CodeStyleManager
.getInstance(myProject
);
90 myJavaCodeStyle
= JavaCodeStyleManager
.getInstance(myProject
);
91 myDescriptiveName
= UsageViewUtil
.getDescriptiveName(myMethod
);
94 protected String
getCommandName() {
95 return RefactoringBundle
.message("inline.method.command", myDescriptiveName
);
98 protected UsageViewDescriptor
createUsageViewDescriptor(UsageInfo
[] usages
) {
99 return new InlineViewDescriptor(myMethod
);
103 protected UsageInfo
[] findUsages() {
104 if (myInlineThisOnly
) return new UsageInfo
[]{new UsageInfo(myReference
)};
105 Set
<UsageInfo
> usages
= new HashSet
<UsageInfo
>();
106 if (myReference
!= null) {
107 usages
.add(new UsageInfo(myReference
));
109 for (PsiReference reference
: ReferencesSearch
.search(myMethod
)) {
110 usages
.add(new UsageInfo(reference
.getElement()));
113 return usages
.toArray(new UsageInfo
[usages
.size()]);
116 protected void refreshElements(PsiElement
[] elements
) {
117 boolean condition
= elements
.length
== 1 && elements
[0] instanceof PsiMethod
;
118 LOG
.assertTrue(condition
);
119 myMethod
= (PsiMethod
)elements
[0];
122 protected boolean preprocessUsages(Ref
<UsageInfo
[]> refUsages
) {
123 UsageInfo
[] usagesIn
= refUsages
.get();
124 MultiMap
<PsiElement
, String
> conflicts
= new MultiMap
<PsiElement
, String
>();
126 if (!myInlineThisOnly
) {
127 final PsiMethod
[] superMethods
= myMethod
.findSuperMethods();
128 for (PsiMethod method
: superMethods
) {
129 final String message
= method
.hasModifierProperty(PsiModifier
.ABSTRACT
) ? RefactoringBundle
130 .message("inlined.method.implements.method.from.0", method
.getContainingClass().getQualifiedName()) : RefactoringBundle
131 .message("inlined.method.overrides.method.from.0", method
.getContainingClass().getQualifiedName());
132 conflicts
.putValue(method
, message
);
136 addInaccessibleMemberConflicts(myMethod
, usagesIn
, new ReferencedElementsCollector(), conflicts
);
138 if (!conflicts
.isEmpty()) {
139 ConflictsDialog dialog
= new ConflictsDialog(myProject
, conflicts
);
141 if (!dialog
.isOK()) {
142 if (dialog
.isShowConflicts()) prepareSuccessful();
147 if (!myInlineThisOnly
) {
148 if (!CommonRefactoringUtil
.checkReadOnlyStatus(myProject
, myMethod
)) return false;
155 public static void addInaccessibleMemberConflicts(final PsiElement element
,
156 final UsageInfo
[] usages
,
157 final ReferencedElementsCollector collector
,
158 final MultiMap
<PsiElement
, String
> conflicts
) {
159 element
.accept(collector
);
160 final Map
<PsiMember
, Set
<PsiMember
>> containersToReferenced
= getInaccessible(collector
.myReferencedMembers
, usages
);
162 final Set
<PsiMember
> containers
= containersToReferenced
.keySet();
163 for (PsiMember container
: containers
) {
164 Set
<PsiMember
> referencedInaccessible
= containersToReferenced
.get(container
);
165 for (PsiMember referenced
: referencedInaccessible
) {
166 final String referencedDescription
= RefactoringUIUtil
.getDescription(referenced
, true);
167 final String containerDescription
= RefactoringUIUtil
.getDescription(container
, true);
168 String message
= RefactoringBundle
.message("0.that.is.used.in.inlined.method.is.not.accessible.from.call.site.s.in.1",
169 referencedDescription
, containerDescription
);
170 conflicts
.putValue(container
, CommonRefactoringUtil
.capitalize(message
));
176 * Given a set of referencedElements, returns a map from containers (in a sense of ConflictsUtil.getContainer)
177 * to subsets of referencedElemens that are not accessible from that container
179 * @param referencedElements
182 private static Map
<PsiMember
, Set
<PsiMember
>> getInaccessible(HashSet
<PsiMember
> referencedElements
, UsageInfo
[] usages
) {
183 Map
<PsiMember
, Set
<PsiMember
>> result
= new HashMap
<PsiMember
, Set
<PsiMember
>>();
185 for (UsageInfo usage
: usages
) {
186 final PsiMember container
= ConflictsUtil
.getContainer(usage
.getElement());
187 if (container
== null) continue; // usage in import statement
188 Set
<PsiMember
> inaccessibleReferenced
= result
.get(container
);
189 if (inaccessibleReferenced
== null) {
190 inaccessibleReferenced
= new HashSet
<PsiMember
>();
191 result
.put(container
, inaccessibleReferenced
);
192 for (PsiMember member
: referencedElements
) {
193 if (!PsiUtil
.isAccessible(member
, usage
.getElement(), null)) {
194 inaccessibleReferenced
.add(member
);
203 protected void performRefactoring(UsageInfo
[] usages
) {
206 if (myEditor
!= null) {
207 col
= myEditor
.getCaretModel().getLogicalPosition().column
;
208 line
= myEditor
.getCaretModel().getLogicalPosition().line
;
209 LogicalPosition pos
= new LogicalPosition(0, 0);
210 myEditor
.getCaretModel().moveToLogicalPosition(pos
);
213 LocalHistoryAction a
= LocalHistory
.startAction(myProject
, getCommandName());
215 doRefactoring(usages
);
221 if (myEditor
!= null) {
222 LogicalPosition pos
= new LogicalPosition(line
, col
);
223 myEditor
.getCaretModel().moveToLogicalPosition(pos
);
227 private void doRefactoring(UsageInfo
[] usages
) {
229 if (myInlineThisOnly
) {
230 if (myMethod
.isConstructor()) {
231 PsiCall constructorCall
= RefactoringUtil
.getEnclosingConstructorCall(myReference
);
232 if (constructorCall
!= null) {
233 inlineConstructorCall(constructorCall
);
237 myReference
= addBracesWhenNeeded(new PsiReferenceExpression
[]{(PsiReferenceExpression
)myReference
})[0];
238 inlineMethodCall((PsiReferenceExpression
)myReference
);
242 RefactoringUtil
.sortDepthFirstRightLeftOrder(usages
);
243 if (myMethod
.isConstructor()) {
244 for (UsageInfo usage
: usages
) {
245 PsiElement element
= usage
.getElement();
246 if (element
instanceof PsiJavaCodeReferenceElement
) {
247 PsiCall constructorCall
= RefactoringUtil
.getEnclosingConstructorCall((PsiJavaCodeReferenceElement
)element
);
248 if (constructorCall
!= null) {
249 inlineConstructorCall(constructorCall
);
252 else if (element
instanceof PsiEnumConstant
) {
253 inlineConstructorCall((PsiEnumConstant
) element
);
259 List
<PsiReferenceExpression
> refExprList
= new ArrayList
<PsiReferenceExpression
>();
260 for (final UsageInfo usage
: usages
) {
261 final PsiElement element
= usage
.getElement();
262 if (element
instanceof PsiReferenceExpression
) {
263 refExprList
.add((PsiReferenceExpression
)element
);
266 PsiReferenceExpression
[] refs
= refExprList
.toArray(new PsiReferenceExpression
[refExprList
.size()]);
267 refs
= addBracesWhenNeeded(refs
);
268 for (PsiReferenceExpression ref
: refs
) {
269 inlineMethodCall(ref
);
274 removeAddedBracesWhenPossible();
276 catch (IncorrectOperationException e
) {
281 public static void inlineConstructorCall(PsiCall constructorCall
) {
282 final PsiMethod oldConstructor
= constructorCall
.resolveMethod();
283 LOG
.assertTrue(oldConstructor
!= null);
284 final PsiExpression
[] instanceCreationArguments
= constructorCall
.getArgumentList().getExpressions();
285 final PsiParameter
[] parameters
= oldConstructor
.getParameterList().getParameters();
286 LOG
.assertTrue(parameters
.length
== instanceCreationArguments
.length
);
288 PsiStatement
[] statements
= oldConstructor
.getBody().getStatements();
289 LOG
.assertTrue(statements
.length
== 1 && statements
[0] instanceof PsiExpressionStatement
);
290 PsiExpression expression
= ((PsiExpressionStatement
)statements
[0]).getExpression();
291 LOG
.assertTrue(expression
instanceof PsiMethodCallExpression
);
292 ChangeContextUtil
.encodeContextInfo(expression
, true);
294 PsiMethodCallExpression methodCall
= (PsiMethodCallExpression
)expression
.copy();
295 final PsiExpression
[] args
= methodCall
.getArgumentList().getExpressions();
296 for (PsiExpression arg
: args
) {
297 replaceParameterReferences(arg
, oldConstructor
, instanceCreationArguments
);
301 final PsiExpressionList exprList
= (PsiExpressionList
) constructorCall
.getArgumentList().replace(methodCall
.getArgumentList());
302 ChangeContextUtil
.decodeContextInfo(exprList
, PsiTreeUtil
.getParentOfType(constructorCall
, PsiClass
.class), null);
304 catch (IncorrectOperationException e
) {
307 ChangeContextUtil
.clearContextInfo(expression
);
310 private static void replaceParameterReferences(final PsiElement element
,
311 final PsiMethod oldConstructor
,
312 final PsiExpression
[] instanceCreationArguments
) {
313 boolean isParameterReference
= false;
314 if (element
instanceof PsiReferenceExpression
) {
315 final PsiReferenceExpression expression
= (PsiReferenceExpression
)element
;
316 PsiElement resolved
= expression
.resolve();
317 if (resolved
instanceof PsiParameter
&&
318 element
.getManager().areElementsEquivalent(((PsiParameter
)resolved
).getDeclarationScope(), oldConstructor
)) {
319 isParameterReference
= true;
320 PsiElement declarationScope
= ((PsiParameter
)resolved
).getDeclarationScope();
321 PsiParameter
[] declarationParameters
= ((PsiMethod
)declarationScope
).getParameterList().getParameters();
322 for (int j
= 0; j
< declarationParameters
.length
; j
++) {
323 if (declarationParameters
[j
] == resolved
) {
325 expression
.replace(instanceCreationArguments
[j
]);
327 catch (IncorrectOperationException e
) {
334 if (!isParameterReference
) {
335 PsiElement child
= element
.getFirstChild();
336 while (child
!= null) {
337 PsiElement next
= child
.getNextSibling();
338 replaceParameterReferences(child
, oldConstructor
, instanceCreationArguments
);
344 private void inlineMethodCall(PsiReferenceExpression ref
) throws IncorrectOperationException
{
345 InlineMethodHandler
.TailCallType tailCall
= InlineMethodHandler
.getTailCallType(ref
);
346 ChangeContextUtil
.encodeContextInfo(myMethod
, false);
347 myMethodCopy
= (PsiMethod
)myMethod
.copy();
348 ChangeContextUtil
.clearContextInfo(myMethod
);
350 PsiMethodCallExpression methodCall
= (PsiMethodCallExpression
)ref
.getParent();
352 PsiSubstitutor callSubstitutor
= getCallSubstitutor(methodCall
);
353 BlockData blockData
= prepareBlock(ref
, callSubstitutor
, methodCall
.getArgumentList(), tailCall
);
354 solveVariableNameConflicts(blockData
.block
, ref
);
355 if (callSubstitutor
!= PsiSubstitutor
.EMPTY
) {
356 substituteMethodTypeParams(blockData
.block
, callSubstitutor
);
358 addParmAndThisVarInitializers(blockData
, methodCall
);
360 PsiElement anchor
= RefactoringUtil
.getParentStatement(methodCall
, true);
361 if (anchor
== null) {
362 PsiEnumConstant enumConstant
= PsiTreeUtil
.getParentOfType(methodCall
, PsiEnumConstant
.class);
363 if (enumConstant
!= null) {
364 PsiExpression returnExpr
= getSimpleReturnedExpression(myMethod
);
365 if (returnExpr
!= null) {
366 methodCall
.replace(returnExpr
);
371 PsiElement anchorParent
= anchor
.getParent();
372 PsiLocalVariable thisVar
= null;
373 PsiLocalVariable
[] parmVars
= new PsiLocalVariable
[blockData
.parmVars
.length
];
374 PsiLocalVariable resultVar
= null;
375 PsiStatement
[] statements
= blockData
.block
.getStatements();
376 if (statements
.length
> 0) {
377 int last
= statements
.length
- 1;
378 /*PsiElement first = statements[0];
379 PsiElement last = statements[statements.length - 1];*/
381 if (statements
.length
> 0 && statements
[statements
.length
- 1] instanceof PsiReturnStatement
&&
382 tailCall
!= InlineMethodHandler
.TailCallType
.Return
) {
388 final PsiElement rBraceOrReturnStatement
=
389 PsiTreeUtil
.skipSiblingsForward(statements
[last
], PsiWhiteSpace
.class, PsiComment
.class);
390 LOG
.assertTrue(rBraceOrReturnStatement
!= null);
391 final PsiElement beforeRBraceStatement
= rBraceOrReturnStatement
.getPrevSibling();
392 LOG
.assertTrue(beforeRBraceStatement
!= null);
393 PsiElement firstAdded
= anchorParent
.addRangeBefore(statements
[first
], beforeRBraceStatement
, anchor
);
395 PsiElement current
= firstAdded
.getPrevSibling();
396 LOG
.assertTrue(current
!= null);
397 if (blockData
.thisVar
!= null) {
398 PsiDeclarationStatement statement
= PsiTreeUtil
.getNextSiblingOfType(current
, PsiDeclarationStatement
.class);
399 thisVar
= (PsiLocalVariable
)statement
.getDeclaredElements()[0];
402 for (int i
= 0; i
< parmVars
.length
; i
++) {
403 PsiDeclarationStatement statement
= PsiTreeUtil
.getNextSiblingOfType(current
, PsiDeclarationStatement
.class);
404 parmVars
[i
] = (PsiLocalVariable
)statement
.getDeclaredElements()[0];
407 if (blockData
.resultVar
!= null) {
408 PsiDeclarationStatement statement
= PsiTreeUtil
.getNextSiblingOfType(current
, PsiDeclarationStatement
.class);
409 resultVar
= (PsiLocalVariable
)statement
.getDeclaredElements()[0];
412 if (statements
.length
> 0) {
413 final PsiStatement lastStatement
= statements
[statements
.length
- 1];
414 if (lastStatement
instanceof PsiReturnStatement
&& tailCall
!= InlineMethodHandler
.TailCallType
.Return
) {
415 final PsiExpression returnValue
= ((PsiReturnStatement
)lastStatement
).getReturnValue();
416 if (returnValue
!= null && PsiUtil
.isStatement(returnValue
)) {
417 PsiExpressionStatement exprStatement
= (PsiExpressionStatement
)myFactory
.createStatementFromText("a;", null);
418 exprStatement
.getExpression().replace(returnValue
);
419 anchorParent
.addBefore(exprStatement
, anchor
);
425 if (methodCall
.getParent() instanceof PsiExpressionStatement
|| tailCall
== InlineMethodHandler
.TailCallType
.Return
) {
426 methodCall
.getParent().delete();
429 if (blockData
.resultVar
!= null) {
430 PsiExpression expr
= myFactory
.createExpressionFromText(blockData
.resultVar
.getName(), null);
431 methodCall
.replace(expr
);
438 PsiClass thisClass
= myMethod
.getContainingClass();
439 PsiExpression thisAccessExpr
;
440 if (thisVar
!= null) {
441 if (!canInlineParmOrThisVariable(thisVar
)) {
442 thisAccessExpr
= myFactory
.createExpressionFromText(thisVar
.getName(), null);
445 thisAccessExpr
= thisVar
.getInitializer();
449 thisAccessExpr
= null;
451 ChangeContextUtil
.decodeContextInfo(anchorParent
, thisClass
, thisAccessExpr
);//todo super should be encoded decoded as well
453 if (thisVar
!= null) {
454 inlineParmOrThisVariable(thisVar
, false);
456 final PsiParameter
[] parameters
= myMethod
.getParameterList().getParameters();
457 for (int i
= 0; i
< parmVars
.length
; i
++) {
458 final PsiParameter parameter
= parameters
[i
];
459 final boolean strictlyFinal
= parameter
.hasModifierProperty(PsiModifier
.FINAL
) && isStrictlyFinal(parameter
);
460 inlineParmOrThisVariable(parmVars
[i
], strictlyFinal
);
462 if (resultVar
!= null) {
463 inlineResultVariable(resultVar
);
466 ChangeContextUtil
.clearContextInfo(anchorParent
);
469 private PsiSubstitutor
getCallSubstitutor(PsiMethodCallExpression methodCall
) {
470 JavaResolveResult resolveResult
= methodCall
.getMethodExpression().advancedResolve(false);
471 LOG
.assertTrue(myManager
.areElementsEquivalent(resolveResult
.getElement(), myMethod
));
472 if (resolveResult
.getSubstitutor() != PsiSubstitutor
.EMPTY
) {
473 Iterator
<PsiTypeParameter
> oldTypeParameters
= PsiUtil
.typeParametersIterator(myMethod
);
474 Iterator
<PsiTypeParameter
> newTypeParameters
= PsiUtil
.typeParametersIterator(myMethodCopy
);
475 PsiSubstitutor substitutor
= resolveResult
.getSubstitutor();
476 while (newTypeParameters
.hasNext()) {
477 final PsiTypeParameter newTypeParameter
= newTypeParameters
.next();
478 final PsiTypeParameter oldTypeParameter
= oldTypeParameters
.next();
479 substitutor
= substitutor
.put(newTypeParameter
, resolveResult
.getSubstitutor().substitute(oldTypeParameter
));
484 return PsiSubstitutor
.EMPTY
;
487 private void substituteMethodTypeParams(PsiElement scope
, final PsiSubstitutor substitutor
) {
488 scope
.accept(new JavaRecursiveElementVisitor() {
489 @Override public void visitTypeElement(PsiTypeElement typeElement
) {
490 PsiType type
= typeElement
.getType();
492 if (type
instanceof PsiClassType
) {
493 JavaResolveResult resolveResult
= ((PsiClassType
)type
).resolveGenerics();
494 PsiElement resolved
= resolveResult
.getElement();
495 if (resolved
instanceof PsiTypeParameter
&& ((PsiTypeParameter
)resolved
).getOwner() == myMethodCopy
) {
496 PsiType newType
= resolveResult
.getSubstitutor().putAll(substitutor
).substitute((PsiTypeParameter
)resolved
);
498 typeElement
.replace(myFactory
.createTypeElement(newType
));
501 catch (IncorrectOperationException e
) {
506 super.visitTypeElement(typeElement
);
511 private boolean isStrictlyFinal(PsiParameter parameter
) {
512 for (PsiReference reference
: ReferencesSearch
.search(parameter
, GlobalSearchScope
.projectScope(myProject
), false)) {
513 final PsiElement refElement
= reference
.getElement();
514 final PsiElement anonymousClass
= PsiTreeUtil
.getParentOfType(refElement
, PsiAnonymousClass
.class);
515 if (anonymousClass
!= null && PsiTreeUtil
.isAncestor(myMethod
, anonymousClass
, true)) {
523 private boolean syncNeeded(final PsiReferenceExpression ref
) {
524 if (!myMethod
.hasModifierProperty(PsiModifier
.SYNCHRONIZED
)) return false;
525 final PsiMethod containingMethod
= Util
.getContainingMethod(ref
);
526 if (containingMethod
== null) return true;
527 if (!containingMethod
.hasModifierProperty(PsiModifier
.SYNCHRONIZED
)) return true;
528 final PsiClass sourceContainingClass
= myMethod
.getContainingClass();
529 final PsiClass targetContainingClass
= containingMethod
.getContainingClass();
530 return !sourceContainingClass
.equals(targetContainingClass
);
533 private BlockData
prepareBlock(PsiReferenceExpression ref
,
534 final PsiSubstitutor callSubstitutor
,
535 final PsiExpressionList argumentList
,
536 final InlineMethodHandler
.TailCallType tailCallType
)
537 throws IncorrectOperationException
{
538 final PsiCodeBlock block
= myMethodCopy
.getBody();
539 final PsiStatement
[] originalStatements
= block
.getStatements();
541 PsiLocalVariable resultVar
= null;
542 PsiType returnType
= callSubstitutor
.substitute(myMethod
.getReturnType());
543 String resultName
= null;
544 final int applicabilityLevel
= PsiUtil
.getApplicabilityLevel(myMethod
, callSubstitutor
, argumentList
);
545 if (returnType
!= null && returnType
!= PsiType
.VOID
&& tailCallType
== InlineMethodHandler
.TailCallType
.None
) {
546 resultName
= myJavaCodeStyle
.propertyNameToVariableName("result", VariableKind
.LOCAL_VARIABLE
);
547 resultName
= myJavaCodeStyle
.suggestUniqueVariableName(resultName
, block
.getFirstChild(), true);
548 PsiDeclarationStatement declaration
= myFactory
.createVariableDeclarationStatement(resultName
, returnType
, null);
549 declaration
= (PsiDeclarationStatement
)block
.addAfter(declaration
, null);
550 resultVar
= (PsiLocalVariable
)declaration
.getDeclaredElements()[0];
553 PsiParameter
[] parms
= myMethodCopy
.getParameterList().getParameters();
554 PsiLocalVariable
[] parmVars
= new PsiLocalVariable
[parms
.length
];
555 for (int i
= parms
.length
- 1; i
>= 0; i
--) {
556 PsiParameter parm
= parms
[i
];
557 String parmName
= parm
.getName();
558 String name
= parmName
;
559 name
= myJavaCodeStyle
.variableNameToPropertyName(name
, VariableKind
.PARAMETER
);
560 name
= myJavaCodeStyle
.propertyNameToVariableName(name
, VariableKind
.LOCAL_VARIABLE
);
561 if (!name
.equals(parmName
)) {
562 name
= myJavaCodeStyle
.suggestUniqueVariableName(name
, block
.getFirstChild(), true);
564 RefactoringUtil
.renameVariableReferences(parm
, name
, GlobalSearchScope
.projectScope(myProject
));
565 PsiType paramType
= parm
.getType();
566 @NonNls String defaultValue
;
567 if (paramType
instanceof PsiEllipsisType
) {
568 final PsiEllipsisType ellipsisType
= (PsiEllipsisType
)paramType
;
569 paramType
= ellipsisType
.toArrayType();
570 if (applicabilityLevel
== MethodCandidateInfo
.ApplicabilityLevel
.VARARGS
) {
571 defaultValue
= "new " + ellipsisType
.getComponentType().getCanonicalText() + "[]{}";
574 defaultValue
= PsiTypesUtil
.getDefaultValueOfType(paramType
);
578 defaultValue
= PsiTypesUtil
.getDefaultValueOfType(paramType
);
581 PsiExpression initializer
= myFactory
.createExpressionFromText(defaultValue
, null);
582 PsiDeclarationStatement declaration
=
583 myFactory
.createVariableDeclarationStatement(name
, callSubstitutor
.substitute(paramType
), initializer
);
584 declaration
= (PsiDeclarationStatement
)block
.addAfter(declaration
, null);
585 parmVars
[i
] = (PsiLocalVariable
)declaration
.getDeclaredElements()[0];
586 PsiUtil
.setModifierProperty(parmVars
[i
], PsiModifier
.FINAL
, parm
.hasModifierProperty(PsiModifier
.FINAL
));
589 PsiLocalVariable thisVar
= null;
590 if (!myMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
591 PsiClass containingClass
= myMethod
.getContainingClass();
593 if (containingClass
!= null) {
594 PsiType thisType
= myFactory
.createType(containingClass
, callSubstitutor
);
595 String
[] names
= myJavaCodeStyle
.suggestVariableName(VariableKind
.LOCAL_VARIABLE
, null, null, thisType
)
597 String thisVarName
= names
[0];
598 thisVarName
= myJavaCodeStyle
.suggestUniqueVariableName(thisVarName
, block
.getFirstChild(), true);
599 PsiExpression initializer
= myFactory
.createExpressionFromText("null", null);
600 PsiDeclarationStatement declaration
= myFactory
.createVariableDeclarationStatement(thisVarName
, thisType
, initializer
);
601 declaration
= (PsiDeclarationStatement
)block
.addAfter(declaration
, null);
602 thisVar
= (PsiLocalVariable
)declaration
.getDeclaredElements()[0];
606 if (thisVar
!= null && syncNeeded(ref
)) {
607 PsiSynchronizedStatement synchronizedStatement
=
608 (PsiSynchronizedStatement
)myFactory
.createStatementFromText("synchronized(" + thisVar
.getName() + "){}", block
);
609 synchronizedStatement
= (PsiSynchronizedStatement
)CodeStyleManager
.getInstance(myProject
).reformat(synchronizedStatement
);
610 synchronizedStatement
= (PsiSynchronizedStatement
)block
.add(synchronizedStatement
);
611 final PsiCodeBlock synchronizedBody
= synchronizedStatement
.getBody();
612 for (final PsiStatement originalStatement
: originalStatements
) {
613 synchronizedBody
.add(originalStatement
);
614 originalStatement
.delete();
618 if (resultName
!= null || tailCallType
== InlineMethodHandler
.TailCallType
.Simple
) {
619 PsiReturnStatement
[] returnStatements
= RefactoringUtil
.findReturnStatements(myMethodCopy
);
620 for (PsiReturnStatement returnStatement
: returnStatements
) {
621 final PsiExpression returnValue
= returnStatement
.getReturnValue();
622 if (returnValue
== null) continue;
623 PsiStatement statement
;
624 if (tailCallType
== InlineMethodHandler
.TailCallType
.Simple
) {
625 if (returnValue
instanceof PsiCallExpression
) {
626 PsiExpressionStatement exprStatement
= (PsiExpressionStatement
) myFactory
.createStatementFromText("a;", null);
627 exprStatement
.getExpression().replace(returnValue
);
628 returnStatement
.getParent().addBefore(exprStatement
, returnStatement
);
630 statement
= myFactory
.createStatementFromText("return;", null);
633 statement
= myFactory
.createStatementFromText(resultName
+ "=0;", null);
634 statement
= (PsiStatement
)myCodeStyleManager
.reformat(statement
);
635 PsiAssignmentExpression assignment
= (PsiAssignmentExpression
)((PsiExpressionStatement
)statement
).getExpression();
636 assignment
.getRExpression().replace(returnValue
);
638 returnStatement
.replace(statement
);
642 return new BlockData(block
, thisVar
, parmVars
, resultVar
);
645 private void solveVariableNameConflicts(PsiElement scope
, final PsiElement placeToInsert
) throws IncorrectOperationException
{
646 if (scope
instanceof PsiVariable
) {
647 PsiVariable var
= (PsiVariable
)scope
;
648 String name
= var
.getName();
649 String oldName
= name
;
651 String newName
= myJavaCodeStyle
.suggestUniqueVariableName(name
, placeToInsert
, true);
652 if (newName
.equals(name
)) break;
654 newName
= myJavaCodeStyle
.suggestUniqueVariableName(name
, var
, true);
655 if (newName
.equals(name
)) break;
658 if (!name
.equals(oldName
)) {
659 RefactoringUtil
.renameVariableReferences(var
, name
, GlobalSearchScope
.projectScope(myProject
));
660 var
.getNameIdentifier().replace(myFactory
.createIdentifier(name
));
664 PsiElement
[] children
= scope
.getChildren();
665 for (PsiElement child
: children
) {
666 solveVariableNameConflicts(child
, placeToInsert
);
670 private void addParmAndThisVarInitializers(BlockData blockData
, PsiMethodCallExpression methodCall
) throws IncorrectOperationException
{
671 PsiExpression
[] args
= methodCall
.getArgumentList().getExpressions();
672 for (int i
= 0; i
< args
.length
; i
++) {
673 int j
= Math
.min(i
, blockData
.parmVars
.length
- 1);
674 final PsiExpression initializer
= blockData
.parmVars
[j
].getInitializer();
675 LOG
.assertTrue(initializer
!= null);
676 if (initializer
instanceof PsiNewExpression
&& ((PsiNewExpression
)initializer
).getArrayInitializer() != null) { //varargs initializer
677 final PsiArrayInitializerExpression arrayInitializer
= ((PsiNewExpression
)initializer
).getArrayInitializer();
678 arrayInitializer
.add(args
[i
]);
682 initializer
.replace(args
[i
]);
685 if (blockData
.thisVar
!= null) {
686 PsiExpression qualifier
= methodCall
.getMethodExpression().getQualifierExpression();
687 if (qualifier
== null) {
688 PsiElement parent
= methodCall
.getParent();
690 if (parent
instanceof PsiClass
) break;
691 if (parent
instanceof PsiFile
) break;
692 parent
= parent
.getParent();
694 if (parent
instanceof PsiClass
) {
695 final PsiClass parentClass
= (PsiClass
)parent
;
696 final PsiClass containingClass
= myMethod
.getContainingClass();
697 if (InheritanceUtil
.isInheritorOrSelf(parentClass
, containingClass
, true)) {
698 qualifier
= myFactory
.createExpressionFromText("this", null);
701 String name
= containingClass
.getName();
703 qualifier
= myFactory
.createExpressionFromText(name
+ ".this", null);
706 qualifier
= myFactory
.createExpressionFromText("this", null);
711 qualifier
= myFactory
.createExpressionFromText("this", null);
714 else if (qualifier
instanceof PsiSuperExpression
) {
715 qualifier
= myFactory
.createExpressionFromText("this", null);
717 blockData
.thisVar
.getInitializer().replace(qualifier
);
721 private boolean canInlineParmOrThisVariable(PsiLocalVariable variable
) {
722 boolean isAccessedForWriting
= false;
723 for (PsiReference ref
: ReferencesSearch
.search(variable
)) {
724 PsiElement refElement
= ref
.getElement();
725 if (refElement
instanceof PsiExpression
) {
726 if (PsiUtil
.isAccessedForWriting((PsiExpression
)refElement
)) {
727 isAccessedForWriting
= true;
732 PsiExpression initializer
= variable
.getInitializer();
733 boolean shouldBeFinal
= variable
.hasModifierProperty(PsiModifier
.FINAL
) && false;
734 return canInlineParmOrThisVariable(initializer
, shouldBeFinal
, false, ReferencesSearch
.search(variable
).findAll().size(), isAccessedForWriting
);
737 private void inlineParmOrThisVariable(PsiLocalVariable variable
, boolean strictlyFinal
) throws IncorrectOperationException
{
738 PsiReference firstRef
= ReferencesSearch
.search(variable
).findFirst();
740 if (firstRef
== null) {
741 variable
.getParent().delete(); //Q: side effects?
746 boolean isAccessedForWriting
= false;
747 final Collection
<PsiReference
> refs
= ReferencesSearch
.search(variable
).findAll();
748 for (PsiReference ref
: refs
) {
749 PsiElement refElement
= ref
.getElement();
750 if (refElement
instanceof PsiExpression
) {
751 if (PsiUtil
.isAccessedForWriting((PsiExpression
)refElement
)) {
752 isAccessedForWriting
= true;
757 PsiExpression initializer
= variable
.getInitializer();
758 boolean shouldBeFinal
= variable
.hasModifierProperty(PsiModifier
.FINAL
) && strictlyFinal
;
759 if (canInlineParmOrThisVariable(initializer
, shouldBeFinal
, strictlyFinal
, refs
.size(), isAccessedForWriting
)) {
761 declareUsedLocalsFinal(initializer
, strictlyFinal
);
763 for (PsiReference ref
: refs
) {
764 final PsiJavaCodeReferenceElement javaRef
= (PsiJavaCodeReferenceElement
)ref
;
765 if (initializer
instanceof PsiThisExpression
&& ((PsiThisExpression
)initializer
).getQualifier() == null) {
766 final PsiClass varThisClass
= RefactoringUtil
.getThisClass(variable
);
767 if (RefactoringUtil
.getThisClass(javaRef
) != varThisClass
) {
768 initializer
= JavaPsiFacade
.getInstance(myManager
.getProject()).getElementFactory().createExpressionFromText(varThisClass
.getName() + ".this", variable
);
772 PsiExpression expr
= InlineUtil
.inlineVariable(variable
, initializer
, javaRef
);
774 //Q: move the following code to some util? (addition to inline?)
775 if (expr
instanceof PsiThisExpression
) {
776 if (expr
.getParent() instanceof PsiReferenceExpression
) {
777 PsiReferenceExpression refExpr
= (PsiReferenceExpression
)expr
.getParent();
778 PsiElement refElement
= refExpr
.resolve();
779 PsiExpression exprCopy
= (PsiExpression
)refExpr
.copy();
780 refExpr
= (PsiReferenceExpression
)refExpr
.replace(myFactory
.createExpressionFromText(refExpr
.getReferenceName(), null));
781 if (refElement
!= null) {
782 PsiElement newRefElement
= refExpr
.resolve();
783 if (!refElement
.equals(newRefElement
)) {
785 refExpr
.replace(exprCopy
);
791 variable
.getParent().delete();
795 private boolean canInlineParmOrThisVariable(PsiExpression initializer
,
796 boolean shouldBeFinal
,
797 boolean strictlyFinal
,
799 boolean isAccessedForWriting
) {
801 class CanAllLocalsBeDeclaredFinal
extends JavaRecursiveElementWalkingVisitor
{
802 boolean success
= true;
804 @Override public void visitReferenceExpression(PsiReferenceExpression expression
) {
805 final PsiElement psiElement
= expression
.resolve();
806 if (psiElement
instanceof PsiLocalVariable
|| psiElement
instanceof PsiParameter
) {
807 if (!RefactoringUtil
.canBeDeclaredFinal((PsiVariable
)psiElement
)) {
813 @Override public void visitElement(PsiElement element
) {
815 super.visitElement(element
);
820 final CanAllLocalsBeDeclaredFinal canAllLocalsBeDeclaredFinal
= new CanAllLocalsBeDeclaredFinal();
821 initializer
.accept(canAllLocalsBeDeclaredFinal
);
822 if (!canAllLocalsBeDeclaredFinal
.success
) return false;
824 if (initializer
instanceof PsiReferenceExpression
) {
825 PsiVariable refVar
= (PsiVariable
)((PsiReferenceExpression
)initializer
).resolve();
826 if (refVar
== null) {
827 return !isAccessedForWriting
;
829 if (refVar
instanceof PsiField
) {
830 if (isAccessedForWriting
) return false;
832 PsiField field = (PsiField)refVar;
833 if (isFieldNonModifiable(field)){
839 return true; //TODO: "suspicous" places to review by user!
842 if (isAccessedForWriting
) {
843 if (refVar
.hasModifierProperty(PsiModifier
.FINAL
) || shouldBeFinal
) return false;
844 PsiReference
[] refs
=
845 ReferencesSearch
.search(refVar
, GlobalSearchScope
.projectScope(myProject
), false).toArray(new PsiReference
[0]);
846 return refs
.length
== 1; //TODO: control flow
850 return refVar
.hasModifierProperty(PsiModifier
.FINAL
) || RefactoringUtil
.canBeDeclaredFinal(refVar
);
856 else if (isAccessedForWriting
) {
859 else if (initializer
instanceof PsiCallExpression
) {
860 if (accessCount
> 1) return false;
861 final PsiExpressionList argumentList
= ((PsiCallExpression
)initializer
).getArgumentList();
862 if (argumentList
== null) return false;
863 final PsiExpression
[] expressions
= argumentList
.getExpressions();
864 for (PsiExpression expression
: expressions
) {
865 if (!canInlineParmOrThisVariable(expression
, shouldBeFinal
, strictlyFinal
, accessCount
, false)) {
869 return true; //TODO: "suspicous" places to review by user!
871 else if (initializer
instanceof PsiLiteralExpression
) {
874 else if (initializer
instanceof PsiArrayAccessExpression
) {
875 final PsiExpression arrayExpression
= ((PsiArrayAccessExpression
)initializer
).getArrayExpression();
876 final PsiExpression indexExpression
= ((PsiArrayAccessExpression
)initializer
).getIndexExpression();
877 return canInlineParmOrThisVariable(arrayExpression
, shouldBeFinal
, strictlyFinal
, accessCount
, false) &&
878 canInlineParmOrThisVariable(indexExpression
, shouldBeFinal
, strictlyFinal
, accessCount
, false);
880 else if (initializer
instanceof PsiParenthesizedExpression
) {
881 PsiExpression expr
= ((PsiParenthesizedExpression
)initializer
).getExpression();
882 return expr
== null || canInlineParmOrThisVariable(expr
, shouldBeFinal
, strictlyFinal
, accessCount
, false);
884 else if (initializer
instanceof PsiTypeCastExpression
) {
885 PsiExpression operand
= ((PsiTypeCastExpression
)initializer
).getOperand();
886 return operand
!= null && canInlineParmOrThisVariable(operand
, shouldBeFinal
, strictlyFinal
, accessCount
, false);
888 else if (initializer
instanceof PsiBinaryExpression
) {
889 PsiBinaryExpression binExpr
= (PsiBinaryExpression
)initializer
;
890 PsiExpression lOperand
= binExpr
.getLOperand();
891 PsiExpression rOperand
= binExpr
.getROperand();
892 return rOperand
!= null && canInlineParmOrThisVariable(lOperand
, shouldBeFinal
, strictlyFinal
, accessCount
, false) &&
893 canInlineParmOrThisVariable(rOperand
, shouldBeFinal
, strictlyFinal
, accessCount
, false);
895 else if (initializer
instanceof PsiClassObjectAccessExpression
) {
898 else if (initializer
instanceof PsiThisExpression
) {
901 else if (initializer
instanceof PsiSuperExpression
) {
909 private static void declareUsedLocalsFinal(PsiElement expr
, boolean strictlyFinal
) throws IncorrectOperationException
{
910 if (expr
instanceof PsiReferenceExpression
) {
911 PsiElement refElement
= ((PsiReferenceExpression
)expr
).resolve();
912 if (refElement
instanceof PsiLocalVariable
|| refElement
instanceof PsiParameter
) {
913 if (strictlyFinal
|| RefactoringUtil
.canBeDeclaredFinal((PsiVariable
)refElement
)) {
914 PsiUtil
.setModifierProperty(((PsiVariable
)refElement
), PsiModifier
.FINAL
, true);
918 PsiElement
[] children
= expr
.getChildren();
919 for (PsiElement child
: children
) {
920 declareUsedLocalsFinal(child
, strictlyFinal
);
925 private boolean isFieldNonModifiable(PsiField field) {
926 if (field.hasModifierProperty(PsiModifier.FINAL)){
929 PsiElement[] refs = myManager.getSearchHelper().findReferences(field, null, false);
930 for(int i = 0; i < refs.length; i++){
931 PsiReferenceExpression ref = (PsiReferenceExpression)refs[i];
932 if (PsiUtil.isAccessedForWriting(ref)) {
933 PsiElement container = ref.getParent();
935 if (container instanceof PsiMethod ||
936 container instanceof PsiField ||
937 container instanceof PsiClassInitializer ||
938 container instanceof PsiFile) break;
939 container = container.getParent();
941 if (container instanceof PsiMethod && ((PsiMethod)container).isConstructor()) continue;
949 private void inlineResultVariable(PsiVariable resultVar
) throws IncorrectOperationException
{
950 PsiAssignmentExpression assignment
= null;
951 PsiReferenceExpression resultUsage
= null;
952 for (PsiReference ref1
: ReferencesSearch
.search(resultVar
, GlobalSearchScope
.projectScope(myProject
), false)) {
953 PsiReferenceExpression ref
= (PsiReferenceExpression
)ref1
;
954 if (ref
.getParent() instanceof PsiAssignmentExpression
&& ((PsiAssignmentExpression
)ref
.getParent()).getLExpression().equals(ref
)) {
955 if (assignment
!= null) {
960 assignment
= (PsiAssignmentExpression
)ref
.getParent();
964 LOG
.assertTrue(resultUsage
== null);
969 if (assignment
== null) return;
970 boolean condition
= assignment
.getParent() instanceof PsiExpressionStatement
;
971 LOG
.assertTrue(condition
);
972 // SCR3175 fixed: inline only if declaration and assignment is in the same code block.
973 if (!(assignment
.getParent().getParent() == resultVar
.getParent().getParent())) return;
974 if (resultUsage
!= null) {
975 String name
= resultVar
.getName();
976 PsiDeclarationStatement declaration
=
977 myFactory
.createVariableDeclarationStatement(name
, resultVar
.getType(), assignment
.getRExpression());
978 declaration
= (PsiDeclarationStatement
)assignment
.getParent().replace(declaration
);
979 resultVar
.getParent().delete();
980 resultVar
= (PsiVariable
)declaration
.getDeclaredElements()[0];
982 PsiElement parentStatement
= RefactoringUtil
.getParentStatement(resultUsage
, true);
983 PsiElement next
= declaration
.getNextSibling();
984 boolean canInline
= false;
986 if (next
== null) break;
987 if (parentStatement
.equals(next
)) {
991 if (next
instanceof PsiStatement
) break;
992 next
= next
.getNextSibling();
996 InlineUtil
.inlineVariable(resultVar
, resultVar
.getInitializer(), resultUsage
);
997 declaration
.delete();
1001 PsiExpression rExpression
= assignment
.getRExpression();
1002 while (rExpression
instanceof PsiReferenceExpression
) rExpression
= ((PsiReferenceExpression
)rExpression
).getQualifierExpression();
1003 if (rExpression
== null || !PsiUtil
.isStatement(rExpression
)) {
1004 assignment
.delete();
1007 assignment
.replace(rExpression
);
1013 private static final Key
<String
> MARK_KEY
= Key
.create("");
1015 private PsiReferenceExpression
[] addBracesWhenNeeded(PsiReferenceExpression
[] refs
) throws IncorrectOperationException
{
1016 ArrayList
<PsiReferenceExpression
> refsVector
= new ArrayList
<PsiReferenceExpression
>();
1017 ArrayList
<PsiBlockStatement
> addedBracesVector
= new ArrayList
<PsiBlockStatement
>();
1018 myAddedClassInitializers
= new HashMap
<PsiField
, PsiClassInitializer
>();
1020 for (PsiReferenceExpression ref
: refs
) {
1021 ref
.putCopyableUserData(MARK_KEY
, "");
1025 for (PsiReferenceExpression ref
: refs
) {
1026 if (!ref
.isValid()) continue;
1028 PsiElement parentStatement
= RefactoringUtil
.getParentStatement(ref
, true);
1029 if (parentStatement
!= null) {
1030 PsiElement parent
= ref
.getParent();
1031 while (!parent
.equals(parentStatement
)) {
1032 if (parent
instanceof PsiStatement
&& !(parent
instanceof PsiDeclarationStatement
)) {
1033 String text
= "{\n}";
1034 PsiBlockStatement blockStatement
= (PsiBlockStatement
)myFactory
.createStatementFromText(text
, null);
1035 blockStatement
= (PsiBlockStatement
)myCodeStyleManager
.reformat(blockStatement
);
1036 blockStatement
.getCodeBlock().add(parent
);
1037 blockStatement
= (PsiBlockStatement
)parent
.replace(blockStatement
);
1039 PsiElement newStatement
= blockStatement
.getCodeBlock().getStatements()[0];
1040 addMarkedElements(refsVector
, newStatement
);
1041 addedBracesVector
.add(blockStatement
);
1044 parent
= parent
.getParent();
1048 final PsiField field
= PsiTreeUtil
.getParentOfType(ref
, PsiField
.class);
1049 if (field
!= null) {
1050 if (field
instanceof PsiEnumConstant
) {
1051 inlineEnumConstantParameter(refsVector
, ref
);
1054 field
.normalizeDeclaration();
1055 final PsiExpression initializer
= field
.getInitializer();
1056 LOG
.assertTrue(initializer
!= null);
1057 PsiClassInitializer classInitializer
= myFactory
.createClassInitializer();
1058 final PsiClass containingClass
= field
.getContainingClass();
1059 classInitializer
= (PsiClassInitializer
)containingClass
.addAfter(classInitializer
, field
);
1060 containingClass
.addAfter(CodeEditUtil
.createLineFeed(field
.getManager()), field
);
1061 final PsiCodeBlock body
= classInitializer
.getBody();
1062 PsiExpressionStatement statement
= (PsiExpressionStatement
)myFactory
.createStatementFromText(field
.getName() + " = 0;", body
);
1063 statement
= (PsiExpressionStatement
)body
.add(statement
);
1064 final PsiAssignmentExpression assignment
= (PsiAssignmentExpression
)statement
.getExpression();
1065 assignment
.getLExpression().replace(RenameJavaVariableProcessor
.createMemberReference(field
, assignment
));
1066 assignment
.getRExpression().replace(initializer
);
1067 addMarkedElements(refsVector
, statement
);
1068 if (field
.hasModifierProperty(PsiModifier
.STATIC
)) {
1069 PsiUtil
.setModifierProperty(classInitializer
, PsiModifier
.STATIC
, true);
1071 myAddedClassInitializers
.put(field
, classInitializer
);
1076 refsVector
.add(ref
);
1079 for (PsiReferenceExpression ref
: refs
) {
1080 ref
.putCopyableUserData(MARK_KEY
, null);
1083 myAddedBraces
= addedBracesVector
.toArray(new PsiBlockStatement
[addedBracesVector
.size()]);
1084 return refsVector
.toArray(new PsiReferenceExpression
[refsVector
.size()]);
1087 private void inlineEnumConstantParameter(final List
<PsiReferenceExpression
> refsVector
,
1088 final PsiReferenceExpression ref
) throws IncorrectOperationException
{
1089 PsiExpression expr
= getSimpleReturnedExpression(myMethod
);
1091 refsVector
.add(ref
);
1094 PsiCall call
= PsiTreeUtil
.getParentOfType(ref
, PsiCall
.class);
1095 @NonNls String text
= "new Object() { " + myMethod
.getReturnTypeElement().getText() + " evaluate() { return " + call
.getText() + ";}}.evaluate";
1096 PsiExpression callExpr
= JavaPsiFacade
.getInstance(myProject
).getParserFacade().createExpressionFromText(text
, call
);
1097 PsiElement classExpr
= ref
.replace(callExpr
);
1098 classExpr
.accept(new JavaRecursiveElementWalkingVisitor() {
1099 public void visitReturnStatement(final PsiReturnStatement statement
) {
1100 super.visitReturnStatement(statement
);
1101 PsiExpression expr
= statement
.getReturnValue();
1102 if (expr
instanceof PsiMethodCallExpression
) {
1103 refsVector
.add(((PsiMethodCallExpression
) expr
).getMethodExpression());
1107 if (classExpr
.getParent() instanceof PsiMethodCallExpression
) {
1108 PsiExpressionList args
= ((PsiMethodCallExpression
)classExpr
.getParent()).getArgumentList();
1109 PsiExpression
[] argExpressions
= args
.getExpressions();
1110 if (argExpressions
.length
> 0) {
1111 args
.deleteChildRange(argExpressions
[0], argExpressions
[argExpressions
.length
-1]);
1118 private static PsiExpression
getSimpleReturnedExpression(final PsiMethod method
) {
1119 PsiCodeBlock body
= method
.getBody();
1120 if (body
== null) return null;
1121 PsiStatement
[] psiStatements
= body
.getStatements();
1122 if (psiStatements
.length
!= 1) return null;
1123 PsiStatement statement
= psiStatements
[0];
1124 if (!(statement
instanceof PsiReturnStatement
)) return null;
1125 return ((PsiReturnStatement
) statement
).getReturnValue();
1128 private static void addMarkedElements(final List
<PsiReferenceExpression
> array
, PsiElement scope
) {
1129 scope
.accept(new PsiRecursiveElementWalkingVisitor() {
1130 @Override public void visitElement(PsiElement element
) {
1131 if (element
.getCopyableUserData(MARK_KEY
) != null) {
1132 array
.add((PsiReferenceExpression
)element
);
1133 element
.putCopyableUserData(MARK_KEY
, null);
1135 super.visitElement(element
);
1140 private void removeAddedBracesWhenPossible() throws IncorrectOperationException
{
1141 if (myAddedBraces
== null) return;
1143 for (PsiBlockStatement blockStatement
: myAddedBraces
) {
1144 PsiStatement
[] statements
= blockStatement
.getCodeBlock().getStatements();
1145 if (statements
.length
== 1) {
1146 blockStatement
.replace(statements
[0]);
1150 final Set
<PsiField
> fields
= myAddedClassInitializers
.keySet();
1152 for (PsiField psiField
: fields
) {
1153 final PsiClassInitializer classInitializer
= myAddedClassInitializers
.get(psiField
);
1154 final PsiExpression initializer
= getSimpleFieldInitializer(psiField
, classInitializer
);
1155 if (initializer
!= null) {
1156 psiField
.getInitializer().replace(initializer
);
1157 classInitializer
.delete();
1160 psiField
.getInitializer().delete();
1166 private PsiExpression
getSimpleFieldInitializer(PsiField field
, PsiClassInitializer initializer
) {
1167 final PsiStatement
[] statements
= initializer
.getBody().getStatements();
1168 if (statements
.length
!= 1) return null;
1169 if (!(statements
[0] instanceof PsiExpressionStatement
)) return null;
1170 final PsiExpression expression
= ((PsiExpressionStatement
)statements
[0]).getExpression();
1171 if (!(expression
instanceof PsiAssignmentExpression
)) return null;
1172 final PsiExpression lExpression
= ((PsiAssignmentExpression
)expression
).getLExpression();
1173 if (!(lExpression
instanceof PsiReferenceExpression
)) return null;
1174 final PsiElement resolved
= ((PsiReferenceExpression
)lExpression
).resolve();
1175 if (!myManager
.areElementsEquivalent(field
, resolved
)) return null;
1176 return ((PsiAssignmentExpression
)expression
).getRExpression();
1179 public static boolean checkBadReturns(PsiMethod method
) {
1180 PsiReturnStatement
[] returns
= RefactoringUtil
.findReturnStatements(method
);
1181 if (returns
.length
== 0) return false;
1182 PsiCodeBlock body
= method
.getBody();
1183 ControlFlow controlFlow
;
1185 controlFlow
= ControlFlowFactory
.getInstance(body
.getProject()).getControlFlow(body
, new LocalsControlFlowPolicy(body
), false);
1187 catch (AnalysisCanceledException e
) {
1190 if (LOG
.isDebugEnabled()) {
1191 LOG
.debug("Control flow:");
1192 LOG
.debug(controlFlow
.toString());
1195 List
<Instruction
> instructions
= new ArrayList
<Instruction
>(controlFlow
.getInstructions());
1197 // temporary replace all return's with empty statements in the flow
1198 for (PsiReturnStatement aReturn
: returns
) {
1199 int offset
= controlFlow
.getStartOffset(aReturn
);
1200 int endOffset
= controlFlow
.getEndOffset(aReturn
);
1201 while (offset
<= endOffset
&& !(instructions
.get(offset
) instanceof GoToInstruction
)) {
1204 LOG
.assertTrue(instructions
.get(offset
) instanceof GoToInstruction
);
1205 instructions
.set(offset
, EmptyInstruction
.INSTANCE
);
1208 for (PsiReturnStatement aReturn
: returns
) {
1209 int offset
= controlFlow
.getEndOffset(aReturn
);
1211 if (offset
== instructions
.size()) break;
1212 Instruction instruction
= instructions
.get(offset
);
1213 if (instruction
instanceof GoToInstruction
) {
1214 offset
= ((GoToInstruction
)instruction
).offset
;
1216 else if (instruction
instanceof ThrowToInstruction
) {
1217 offset
= ((ThrowToInstruction
)instruction
).offset
;
1219 else if (instruction
instanceof ConditionalThrowToInstruction
) {
1220 // In case of "conditional throw to", control flow will not be altered
1221 // If exception handler is in method, we will inline it to ivokation site
1222 // If exception handler is at invocation site, execution will continue to get there
1234 private static class BlockData
{
1235 final PsiCodeBlock block
;
1236 final PsiLocalVariable thisVar
;
1237 final PsiLocalVariable
[] parmVars
;
1238 final PsiLocalVariable resultVar
;
1240 public BlockData(PsiCodeBlock block
, PsiLocalVariable thisVar
, PsiLocalVariable
[] parmVars
, PsiLocalVariable resultVar
) {
1242 this.thisVar
= thisVar
;
1243 this.parmVars
= parmVars
;
1244 this.resultVar
= resultVar
;
1249 protected Collection
<?
extends PsiElement
> getElementsToWrite(@NotNull final UsageViewDescriptor descriptor
) {
1250 if (myInlineThisOnly
) {
1251 return Collections
.singletonList(myReference
);
1254 return myReference
== null ? Collections
.singletonList(myMethod
) : Arrays
.asList(myReference
, myMethod
);